Merge "Synchronize SV buffers with main window when config changes"
diff --git a/Android.bp b/Android.bp
index d52f08f..778aa55 100644
--- a/Android.bp
+++ b/Android.bp
@@ -82,6 +82,7 @@
":framework-mca-filterpacks-sources",
":framework-media-sources",
":framework-mms-sources",
+ ":framework-omapi-sources",
":framework-opengl-sources",
":framework-rs-sources",
":framework-sax-sources",
@@ -273,6 +274,7 @@
"android.hardware.vibrator-V1.2-java",
"android.hardware.vibrator-V1.3-java",
"android.hardware.vibrator-V2-java",
+ "android.se.omapi-V1-java",
"android.system.suspend.control.internal-java",
"devicepolicyprotosnano",
@@ -324,8 +326,12 @@
"error_prone_android_framework",
],
required: [
+ // TODO(b/120066492): remove default_television.xml when the build system
+ // propagates "required" properly.
+ "default_television.xml",
"framework-platform-compat-config",
- // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
+ // TODO(b/120066492): remove gps_debug and protolog.conf.json when the build
+ // system propagates "required" properly.
"gps_debug.conf",
"icu4j-platform-compat-config",
"protolog.conf.json.gz",
@@ -378,6 +384,9 @@
"//frameworks/base/packages/Tethering/tests/unit",
"//packages/modules/Connectivity/Tethering/tests/unit",
],
+ lint: {
+ extra_check_modules: ["AndroidFrameworkLintChecker"],
+ },
errorprone: {
javacflags: [
"-Xep:AndroidFrameworkBinderIdentity:ERROR",
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 392df73..1620983 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5142,7 +5142,7 @@
Slog.d(TAG, "mBroadcastRefCount -> " + mBroadcastRefCount);
}
if (mBroadcastRefCount == 0) {
- mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 0).sendToTarget();
+ mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 0, 0).sendToTarget();
mWakeLock.release();
if (mInFlight.size() > 0) {
mLog.w("Finished all dispatches with " + mInFlight.size()
@@ -5314,7 +5314,7 @@
if (mBroadcastRefCount == 0) {
setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true);
mWakeLock.acquire();
- mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1).sendToTarget();
+ mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget();
}
final InFlight inflight = new InFlight(AlarmManagerService.this, alarm, nowELAPSED);
mInFlight.add(inflight);
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 ef442f0..3da508d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1371,7 +1371,11 @@
jobStatus.hasContentTriggerConstraint(),
jobStatus.isRequestedExpeditedJob(),
/* isRunningAsExpeditedJob */ false,
- JobProtoEnums.STOP_REASON_UNDEFINED);
+ JobProtoEnums.STOP_REASON_UNDEFINED,
+ jobStatus.getJob().isPrefetch(),
+ jobStatus.getJob().getPriority(),
+ jobStatus.getEffectivePriority(),
+ jobStatus.getNumFailures());
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
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 b44178f..9cae8645 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -356,7 +356,11 @@
job.hasContentTriggerConstraint(),
job.isRequestedExpeditedJob(),
job.shouldTreatAsExpeditedJob(),
- JobProtoEnums.STOP_REASON_UNDEFINED);
+ JobProtoEnums.STOP_REASON_UNDEFINED,
+ job.getJob().isPrefetch(),
+ job.getJob().getPriority(),
+ job.getEffectivePriority(),
+ job.getNumFailures());
try {
mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
} catch (RemoteException e) {
@@ -1028,7 +1032,11 @@
completedJob.hasContentTriggerConstraint(),
completedJob.isRequestedExpeditedJob(),
completedJob.startedAsExpeditedJob,
- mParams.getStopReason());
+ mParams.getStopReason(),
+ completedJob.getJob().isPrefetch(),
+ completedJob.getJob().getPriority(),
+ completedJob.getEffectivePriority(),
+ completedJob.getNumFailures());
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
internalStopReason);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index 7d12b95..d9c4632 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -50,6 +50,12 @@
{"include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests#testMeteredNetworkAccess_expeditedJob"},
{"include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests#testNonMeteredNetworkAccess_expeditedJob"}
]
+ },
+ {
+ "name": "CtsStatsdAtomHostTestCases",
+ "options": [
+ {"include-filter": "android.cts.statsdatom.jobscheduler"}
+ ]
}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 788bfe4..9749c80 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -28,6 +28,7 @@
import android.app.job.JobInfo;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener;
+import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
@@ -63,6 +64,13 @@
private final PcConstants mPcConstants;
private final PcHandler mHandler;
+ // Note: when determining prefetch bit satisfaction, we mark the bit as satisfied for apps with
+ // active widgets assuming that any prefetch jobs are being used for the widget. However, we
+ // don't have a callback telling us when widget status changes, which is incongruent with the
+ // aforementioned assumption. This inconsistency _should_ be fine since any jobs scheduled
+ // before the widget is activated are definitely not for the widget and don't have to be updated
+ // to "satisfied=true".
+ private AppWidgetManager mAppWidgetManager;
private final UsageStatsManagerInternal mUsageStatsManagerInternal;
@GuardedBy("mLock")
@@ -118,6 +126,11 @@
}
@Override
+ public void onSystemServicesReady() {
+ mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+ }
+
+ @Override
@GuardedBy("mLock")
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
if (jobStatus.getJob().isPrefetch()) {
@@ -298,11 +311,23 @@
// Mark a prefetch constraint as satisfied in the following scenarios:
// 1. The app is not open but it will be launched soon
// 2. The app is open and the job is already running (so we let it finish)
+ // 3. The app is not open but has an active widget (we can't tell if a widget displays
+ // status/data, so this assumes the prefetch job is to update the data displayed on
+ // the widget).
final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid());
final boolean satisfied;
if (!appIsOpen) {
- satisfied = willBeLaunchedSoonLocked(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), now);
+ final int userId = jobStatus.getSourceUserId();
+ final String pkgName = jobStatus.getSourcePackageName();
+ satisfied = willBeLaunchedSoonLocked(userId, pkgName, now)
+ // At the time of implementation, isBoundWidgetPackage() results in a process ID
+ // check and then a lookup into a map. Calling the method here every time
+ // is based on the assumption that widgets won't change often and
+ // AppWidgetManager won't be a bottleneck, so having a local cache won't provide
+ // huge performance gains. If anything changes, we should reconsider having a
+ // local cache.
+ || (mAppWidgetManager != null
+ && mAppWidgetManager.isBoundWidgetPackage(pkgName, userId));
} else {
satisfied = mService.isCurrentlyRunningLocked(jobStatus);
}
diff --git a/apex/media/framework/java/android/media/MediaTranscodingManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java
index 3bfffbcd..aff3204 100644
--- a/apex/media/framework/java/android/media/MediaTranscodingManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodingManager.java
@@ -949,6 +949,8 @@
*
* @return the video track format to be used if transcoding should be performed,
* and null otherwise.
+ * @throws IllegalArgumentException if the hinted source video format contains invalid
+ * parameters.
*/
@Nullable
public MediaFormat resolveVideoFormat() {
@@ -959,20 +961,19 @@
MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint);
videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
- int width = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_WIDTH);
- int height = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_HEIGHT);
+ int width = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_WIDTH, -1);
+ int height = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_HEIGHT, -1);
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException(
"Source Width and height must be larger than 0");
}
- float frameRate = 30.0f; // default to 30fps.
- if (mSrcVideoFormatHint.containsKey(MediaFormat.KEY_FRAME_RATE)) {
- frameRate = mSrcVideoFormatHint.getFloat(MediaFormat.KEY_FRAME_RATE);
- if (frameRate <= 0) {
- throw new IllegalArgumentException(
- "frameRate must be larger than 0");
- }
+ float frameRate =
+ mSrcVideoFormatHint.getNumber(MediaFormat.KEY_FRAME_RATE, 30.0)
+ .floatValue();
+ if (frameRate <= 0) {
+ throw new IllegalArgumentException(
+ "frameRate must be larger than 0");
}
int bitrate = getAVCBitrate(width, height, frameRate);
diff --git a/boot/boot-image-profile.txt b/boot/boot-image-profile.txt
index 5f27cc7..82269d4 100644
--- a/boot/boot-image-profile.txt
+++ b/boot/boot-image-profile.txt
@@ -2899,10 +2899,8 @@
HSPLandroid/app/assist/AssistStructure$ViewNode;->getChildCount()I
HSPLandroid/app/assist/AssistStructure$ViewNode;->writeSelfToParcel(Landroid/os/Parcel;Landroid/os/PooledStringWriter;Z[FZ)I+]Landroid/view/autofill/AutofillId;Landroid/view/autofill/AutofillId;]Landroid/graphics/Matrix;Landroid/graphics/Matrix;]Landroid/app/assist/AssistStructure$ViewNodeText;Landroid/app/assist/AssistStructure$ViewNodeText;]Landroid/os/Parcel;Landroid/os/Parcel;
HSPLandroid/app/assist/AssistStructure$ViewNode;->writeString(Landroid/os/Parcel;Landroid/os/PooledStringWriter;Ljava/lang/String;)V+]Landroid/os/PooledStringWriter;Landroid/os/PooledStringWriter;]Landroid/os/Parcel;Landroid/os/Parcel;
-HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;-><init>()V
HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getChildCount()I
HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getNodeText()Landroid/app/assist/AssistStructure$ViewNodeText;
-HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getViewNode()Landroid/app/assist/AssistStructure$ViewNode;
HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->newChild(I)Landroid/view/ViewStructure;
HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->setAutofillHints([Ljava/lang/String;)V
HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->setAutofillId(Landroid/view/autofill/AutofillId;)V
@@ -3883,8 +3881,6 @@
HSPLandroid/content/ContentResolver$ResultListener;-><init>(Landroid/content/ContentResolver$1;)V
HSPLandroid/content/ContentResolver$ResultListener;->onResult(Landroid/os/Bundle;)V+]Landroid/os/ParcelableException;Landroid/os/ParcelableException;]Ljava/lang/Object;Landroid/content/ContentResolver$StringResultListener;,Landroid/content/ContentResolver$UriResultListener;]Landroid/os/Bundle;Landroid/os/Bundle;]Landroid/content/ContentResolver$ResultListener;Landroid/content/ContentResolver$UriResultListener;,Landroid/content/ContentResolver$StringResultListener;
HSPLandroid/content/ContentResolver$ResultListener;->waitForResult(J)V+]Ljava/lang/Object;Landroid/content/ContentResolver$StringResultListener;
-HSPLandroid/content/ContentResolver$StringResultListener;-><init>()V
-HSPLandroid/content/ContentResolver$StringResultListener;-><init>(Landroid/content/ContentResolver$1;)V
HSPLandroid/content/ContentResolver$StringResultListener;->getResultFromBundle(Landroid/os/Bundle;)Ljava/lang/Object;+]Landroid/content/ContentResolver$StringResultListener;Landroid/content/ContentResolver$StringResultListener;
HSPLandroid/content/ContentResolver$StringResultListener;->getResultFromBundle(Landroid/os/Bundle;)Ljava/lang/String;+]Landroid/os/Bundle;Landroid/os/Bundle;
HSPLandroid/content/ContentResolver;-><init>(Landroid/content/Context;)V
@@ -7029,7 +7025,6 @@
HSPLandroid/graphics/Region$1;->createFromParcel(Landroid/os/Parcel;)Landroid/graphics/Region;
HSPLandroid/graphics/Region$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;+]Landroid/graphics/Region$1;Landroid/graphics/Region$1;
HSPLandroid/graphics/Region;-><init>()V
-HSPLandroid/graphics/Region;-><init>(IIII)V
HSPLandroid/graphics/Region;-><init>(J)V
HSPLandroid/graphics/Region;->access$000(Landroid/os/Parcel;)J
HSPLandroid/graphics/Region;->equals(Ljava/lang/Object;)Z
@@ -7075,7 +7070,6 @@
HSPLandroid/graphics/RenderNode;->hasDisplayList()Z
HSPLandroid/graphics/RenderNode;->hasIdentityMatrix()Z
HSPLandroid/graphics/RenderNode;->isAttached()Z+]Landroid/graphics/RenderNode$AnimationHost;Landroid/view/ViewAnimationHostBridge;
-HSPLandroid/graphics/RenderNode;->isPivotExplicitlySet()Z
HSPLandroid/graphics/RenderNode;->offsetTopAndBottom(I)Z
HSPLandroid/graphics/RenderNode;->setAlpha(F)Z
HSPLandroid/graphics/RenderNode;->setAnimationMatrix(Landroid/graphics/Matrix;)Z+]Landroid/graphics/Matrix;Landroid/graphics/Matrix;
@@ -8283,7 +8277,6 @@
HSPLandroid/hardware/GeomagneticField$LegendreTable;-><init>(IF)V
HSPLandroid/hardware/GeomagneticField;-><init>(FFFJ)V
HSPLandroid/hardware/GeomagneticField;->computeGeocentricCoordinates(FFF)V
-HSPLandroid/hardware/GeomagneticField;->getDeclination()F
HSPLandroid/hardware/HardwareBuffer$1;->createFromParcel(Landroid/os/Parcel;)Landroid/hardware/HardwareBuffer;
HSPLandroid/hardware/HardwareBuffer$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;
HSPLandroid/hardware/HardwareBuffer;-><init>(J)V+]Llibcore/util/NativeAllocationRegistry;Llibcore/util/NativeAllocationRegistry;]Ljava/lang/Class;Ljava/lang/Class;]Ldalvik/system/CloseGuard;Ldalvik/system/CloseGuard;
@@ -11353,7 +11346,6 @@
HSPLandroid/media/SoundPool$EventHandler;->handleMessage(Landroid/os/Message;)V
HSPLandroid/media/SoundPool;-><init>(ILandroid/media/AudioAttributes;)V
HSPLandroid/media/SoundPool;-><init>(ILandroid/media/AudioAttributes;Landroid/media/SoundPool$1;)V
-HSPLandroid/media/SoundPool;->load(Ljava/lang/String;I)I
HSPLandroid/media/SoundPool;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
HSPLandroid/media/SoundPool;->setOnLoadCompleteListener(Landroid/media/SoundPool$OnLoadCompleteListener;)V
HSPLandroid/media/SubtitleController$1;->handleMessage(Landroid/os/Message;)Z
@@ -11389,10 +11381,8 @@
HSPLandroid/media/Utils;->sortDistinctRanges([Landroid/util/Range;)V
HSPLandroid/media/audiofx/AudioEffect$Descriptor;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
HSPLandroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;-><init>(II[Landroid/media/AudioAttributes;)V
-HSPLandroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;->supportsStreamType(I)Z
HSPLandroid/media/audiopolicy/AudioProductStrategy;-><init>(Ljava/lang/String;I[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;)V
HSPLandroid/media/audiopolicy/AudioProductStrategy;->attributesMatches(Landroid/media/AudioAttributes;Landroid/media/AudioAttributes;)Z+]Landroid/media/AudioAttributes;Landroid/media/AudioAttributes;
-HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioAttributesForLegacyStreamType(I)Landroid/media/AudioAttributes;+]Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;
HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioAttributesForStrategyWithLegacyStreamType(I)Landroid/media/AudioAttributes;
HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioProductStrategies()Ljava/util/List;
HSPLandroid/media/audiopolicy/AudioProductStrategy;->getLegacyStreamTypeForStrategyWithAudioAttributes(Landroid/media/AudioAttributes;)I+]Landroid/media/audiopolicy/AudioProductStrategy;Landroid/media/audiopolicy/AudioProductStrategy;]Ljava/util/List;Ljava/util/ArrayList;]Ljava/util/Iterator;Ljava/util/ArrayList$Itr;
@@ -13125,7 +13115,6 @@
HSPLandroid/os/ParcelableParcel$1;->createFromParcel(Landroid/os/Parcel;)Landroid/os/ParcelableParcel;
HSPLandroid/os/ParcelableParcel$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;
HSPLandroid/os/ParcelableParcel;-><init>(Landroid/os/Parcel;Ljava/lang/ClassLoader;)V
-HSPLandroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V
HSPLandroid/os/ParcelableParcel;->getClassLoader()Ljava/lang/ClassLoader;
HSPLandroid/os/ParcelableParcel;->getParcel()Landroid/os/Parcel;
HSPLandroid/os/ParcelableParcel;->writeToParcel(Landroid/os/Parcel;I)V
@@ -13481,7 +13470,6 @@
HSPLandroid/os/Temperature;-><init>(FILjava/lang/String;I)V
HSPLandroid/os/Temperature;->getStatus()I
HSPLandroid/os/Temperature;->isValidStatus(I)Z
-HSPLandroid/os/Temperature;->isValidType(I)Z
HSPLandroid/os/ThreadLocalWorkSource$$ExternalSyntheticLambda0;->get()Ljava/lang/Object;
HSPLandroid/os/ThreadLocalWorkSource;->getToken()J+]Ljava/lang/Integer;Ljava/lang/Integer;]Ljava/lang/ThreadLocal;Ljava/lang/ThreadLocal$SuppliedThreadLocal;
HSPLandroid/os/ThreadLocalWorkSource;->getUid()I+]Ljava/lang/Integer;Ljava/lang/Integer;]Ljava/lang/ThreadLocal;Ljava/lang/ThreadLocal$SuppliedThreadLocal;
@@ -18488,7 +18476,6 @@
HSPLandroid/view/ViewRootImpl$ImeInputStage;-><init>(Landroid/view/ViewRootImpl;Landroid/view/ViewRootImpl$InputStage;Ljava/lang/String;)V
HSPLandroid/view/ViewRootImpl$ImeInputStage;->onFinishedInputEvent(Ljava/lang/Object;Z)V
HSPLandroid/view/ViewRootImpl$ImeInputStage;->onProcess(Landroid/view/ViewRootImpl$QueuedInputEvent;)I
-HSPLandroid/view/ViewRootImpl$InputMetricsListener;-><init>(Landroid/view/ViewRootImpl;)V
HSPLandroid/view/ViewRootImpl$InputMetricsListener;->onFrameMetricsAvailable(I)V+]Landroid/view/ViewRootImpl$WindowInputEventReceiver;Landroid/view/ViewRootImpl$WindowInputEventReceiver;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Landroid/view/InputEventReceiver;Landroid/view/ViewRootImpl$WindowInputEventReceiver;
HSPLandroid/view/ViewRootImpl$InputStage;-><init>(Landroid/view/ViewRootImpl;Landroid/view/ViewRootImpl$InputStage;)V
HSPLandroid/view/ViewRootImpl$InputStage;->apply(Landroid/view/ViewRootImpl$QueuedInputEvent;I)V+]Landroid/view/ViewRootImpl$InputStage;megamorphic_types
@@ -20395,7 +20382,6 @@
HSPLandroid/widget/OverScroller$SplineOverScroller;->fling(IIIII)V
HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineDeceleration(I)D
HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineFlingDistance(I)D
-HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineFlingDuration(I)I
HSPLandroid/widget/OverScroller$SplineOverScroller;->onEdgeReached()V
HSPLandroid/widget/OverScroller$SplineOverScroller;->springback(III)Z
HSPLandroid/widget/OverScroller$SplineOverScroller;->startScroll(III)V
@@ -22568,10 +22554,7 @@
HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$1;->onIsNonStrongBiometricAllowedChanged(ZI)V
HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$1;->onStrongAuthRequiredChanged(II)V
HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$H;->handleMessage(Landroid/os/Message;)V
-HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;-><init>(Landroid/content/Context;)V
-HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;-><init>(Landroid/content/Context;Landroid/os/Looper;)V
HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->getStrongAuthForUser(I)I+]Landroid/util/SparseIntArray;Landroid/util/SparseIntArray;
-HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->getStub()Landroid/app/trust/IStrongAuthTracker$Stub;
HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->handleIsNonStrongBiometricAllowedChanged(ZI)V
HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->handleStrongAuthRequiredChanged(II)V
HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->isNonStrongBiometricAllowedAfterIdleTimeout(I)Z
diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt
index 002d42d..522e88f 100644
--- a/boot/hiddenapi/hiddenapi-unsupported.txt
+++ b/boot/hiddenapi/hiddenapi-unsupported.txt
@@ -124,10 +124,8 @@
Landroid/content/pm/IPackageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageManager$Stub$Proxy;->checkUidPermission(Ljava/lang/String;I)I
Landroid/content/pm/IPackageManager$Stub$Proxy;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
-Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstallLocation()I
Landroid/content/pm/IPackageManager$Stub$Proxy;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
-Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
Landroid/content/pm/IPackageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageManager;
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 59f602c..af4053f 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -1576,7 +1576,7 @@
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr);
- } while (err<0 && errno == EINTR);
+ } while (err == EINTR);
}
checkExit();
diff --git a/core/api/current.txt b/core/api/current.txt
index b9ade0b..3e9d7b3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -129,6 +129,7 @@
field public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
field public static final String READ_LOGS = "android.permission.READ_LOGS";
+ field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY";
field public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS";
field public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
field public static final String READ_PRECISE_PHONE_STATE = "android.permission.READ_PRECISE_PHONE_STATE";
@@ -3255,6 +3256,28 @@
method public boolean willContinue();
}
+ public final class MagnificationConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public float getCenterX();
+ method public float getCenterY();
+ method public int getMode();
+ method public float getScale();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.MagnificationConfig> CREATOR;
+ field public static final int DEFAULT_MODE = 0; // 0x0
+ field public static final int FULLSCREEN_MODE = 1; // 0x1
+ field public static final int WINDOW_MODE = 2; // 0x2
+ }
+
+ public static final class MagnificationConfig.Builder {
+ ctor public MagnificationConfig.Builder();
+ method @NonNull public android.accessibilityservice.MagnificationConfig build();
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setCenterX(float);
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setCenterY(float);
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setMode(int);
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setScale(float);
+ }
+
public final class TouchInteractionController {
method public int getDisplayId();
method public int getMaxPointerCount();
@@ -7226,8 +7249,8 @@
method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
- method public int getNearbyAppStreamingPolicy();
- method public int getNearbyNotificationStreamingPolicy();
+ method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyAppStreamingPolicy();
+ method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyNotificationStreamingPolicy();
method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
method @Nullable public CharSequence getOrganizationName(@NonNull android.content.ComponentName);
method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(@NonNull android.content.ComponentName);
@@ -8720,6 +8743,7 @@
method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method @Deprecated public static android.bluetooth.BluetoothAdapter getDefaultAdapter();
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public java.time.Duration getDiscoverableTimeout();
method public int getLeMaximumAdvertisingDataLength();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getProfileConnectionState(int);
@@ -9014,11 +9038,15 @@
public final class BluetoothClass implements android.os.Parcelable {
method public int describeContents();
+ method public boolean doesClassMatch(int);
method public int getDeviceClass();
method public int getMajorDeviceClass();
method public boolean hasService(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothClass> CREATOR;
+ field public static final int PROFILE_A2DP = 1; // 0x1
+ field public static final int PROFILE_HEADSET = 0; // 0x0
+ field public static final int PROFILE_HID = 3; // 0x3
}
public static class BluetoothClass.Device {
@@ -9332,7 +9360,8 @@
method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int notifyCharacteristicChanged(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothGattCharacteristic, boolean, @NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy(android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeService(android.bluetooth.BluetoothGattService);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
@@ -9926,6 +9955,16 @@
package android.companion {
+ public final class AssociationInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.net.MacAddress getDeviceMacAddress();
+ method @Nullable public String getDeviceProfile();
+ method @Nullable public CharSequence getDisplayName();
+ method public int getId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationInfo> CREATOR;
+ }
+
public final class AssociationRequest implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -9938,6 +9977,7 @@
method @NonNull public android.companion.AssociationRequest.Builder addDeviceFilter(@Nullable android.companion.DeviceFilter<?>);
method @NonNull public android.companion.AssociationRequest build();
method @NonNull public android.companion.AssociationRequest.Builder setDeviceProfile(@NonNull String);
+ method @NonNull public android.companion.AssociationRequest.Builder setDisplayName(@NonNull CharSequence);
method @NonNull public android.companion.AssociationRequest.Builder setSingleDevice(boolean);
}
@@ -9973,20 +10013,26 @@
}
public final class CompanionDeviceManager {
- method @RequiresPermission(value=android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
- method public void disassociate(@NonNull String);
- method @NonNull public java.util.List<java.lang.String> getAssociations();
+ method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING", "android.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.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING", "android.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 @Deprecated public void disassociate(@NonNull String);
+ method public void disassociate(int);
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations();
+ method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
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 @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
- field public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
+ field public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION";
+ field @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
}
public abstract static class CompanionDeviceManager.Callback {
ctor public CompanionDeviceManager.Callback();
- method public abstract void onDeviceFound(android.content.IntentSender);
- method public abstract void onFailure(CharSequence);
+ method public void onAssociationCreated(@NonNull android.companion.AssociationInfo);
+ method public void onAssociationPending(@NonNull android.content.IntentSender);
+ method @Deprecated public void onDeviceFound(@NonNull android.content.IntentSender);
+ method public abstract void onFailure(@Nullable CharSequence);
}
public abstract class CompanionDeviceService extends android.app.Service {
@@ -11279,6 +11325,7 @@
field public static final String ACTION_QUICK_VIEW = "android.intent.action.QUICK_VIEW";
field public static final String ACTION_REBOOT = "android.intent.action.REBOOT";
field public static final String ACTION_RUN = "android.intent.action.RUN";
+ field public static final String ACTION_SAFETY_CENTER = "android.intent.action.SAFETY_CENTER";
field public static final String ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF";
field public static final String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
field public static final String ACTION_SEARCH = "android.intent.action.SEARCH";
@@ -12532,6 +12579,7 @@
method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException;
method public void removeChildSessionId(int);
method public void removeSplit(@NonNull String) throws java.io.IOException;
+ method public void requestChecksums(@NonNull String, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, java.io.FileNotFoundException;
method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
method public void setStagingProgress(float);
method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -13175,6 +13223,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
field public static final int TYPE_BUILTIN = 0; // 0x0
field public static final int TYPE_DYNAMIC = 1; // 0x1
+ field public static final int TYPE_SDK = 3; // 0x3
field public static final int TYPE_STATIC = 2; // 0x2
field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
}
@@ -16487,11 +16536,13 @@
public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
ctor public AdaptiveIconDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+ ctor public AdaptiveIconDrawable(@Nullable android.graphics.drawable.Drawable, @Nullable android.graphics.drawable.Drawable, @Nullable android.graphics.drawable.Drawable);
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getBackground();
method public static float getExtraInsetFraction();
method public android.graphics.drawable.Drawable getForeground();
method public android.graphics.Path getIconMask();
+ method @Nullable public android.graphics.drawable.Drawable getMonochrome();
method public int getOpacity();
method public void invalidateDrawable(@NonNull android.graphics.drawable.Drawable);
method public void scheduleDrawable(@NonNull android.graphics.drawable.Drawable, @NonNull Runnable, long);
@@ -20660,6 +20711,7 @@
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public static boolean isOffloadedPlaybackSupported(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
+ method public boolean isRampingRingerEnabled();
method public boolean isSpeakerphoneOn();
method public boolean isStreamMute(int);
method public boolean isSurroundFormatEnabled(int);
@@ -27638,6 +27690,7 @@
field public static final String CATEGORY_PAYMENT = "payment";
field public static final String EXTRA_CATEGORY = "category";
field public static final String EXTRA_SERVICE_COMPONENT = "component";
+ field public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
@@ -31738,6 +31791,7 @@
method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
+ method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
method @Deprecated @Nullable public java.io.Serializable readSerializable();
@@ -32347,6 +32401,7 @@
field public static final String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
field public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
field public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
+ field public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI = "no_sharing_admin_configured_wifi";
field public static final String DISALLOW_SMS = "no_sms";
field public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
field public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
@@ -32383,13 +32438,16 @@
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationAttributes> CREATOR;
field public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 1; // 0x1
+ field public static final int USAGE_ACCESSIBILITY = 66; // 0x42
field public static final int USAGE_ALARM = 17; // 0x11
field public static final int USAGE_CLASS_ALARM = 1; // 0x1
field public static final int USAGE_CLASS_FEEDBACK = 2; // 0x2
field public static final int USAGE_CLASS_MASK = 15; // 0xf
+ field public static final int USAGE_CLASS_MEDIA = 3; // 0x3
field public static final int USAGE_CLASS_UNKNOWN = 0; // 0x0
field public static final int USAGE_COMMUNICATION_REQUEST = 65; // 0x41
field public static final int USAGE_HARDWARE_FEEDBACK = 50; // 0x32
+ field public static final int USAGE_MEDIA = 19; // 0x13
field public static final int USAGE_NOTIFICATION = 49; // 0x31
field public static final int USAGE_PHYSICAL_EMULATION = 34; // 0x22
field public static final int USAGE_RINGTONE = 33; // 0x21
@@ -35571,7 +35629,7 @@
field public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
field public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
field public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
- field public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
+ field @Deprecated public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
field public static final String AUTO_TIME = "auto_time";
field public static final String AUTO_TIME_ZONE = "auto_time_zone";
field public static final String BLUETOOTH_ON = "bluetooth_on";
@@ -40677,7 +40735,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
method public String getDefaultDialerPackage();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(String);
- method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
@@ -42745,7 +42803,7 @@
method @Nullable public String getMccString();
method @Deprecated public int getMnc();
method @Nullable public String getMncString();
- method public String getNumber();
+ method @Deprecated public String getNumber();
method public int getPortIndex();
method public int getSimSlotIndex();
method public int getSubscriptionId();
@@ -43000,7 +43058,7 @@
method public String getIccAuthentication(int, int, String);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei(int);
- method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public String getManualNetworkSelectionPlmn();
method @Nullable public String getManufacturerCode();
method @Nullable public String getManufacturerCode(int);
@@ -43085,7 +43143,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
- method public boolean setLine1NumberForDisplay(String, String);
+ method @Deprecated public boolean setLine1NumberForDisplay(String, String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(String, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, boolean, int);
@@ -51129,6 +51187,7 @@
method public int getRecommendedTimeoutMillis(int, int);
method public void interrupt();
method public static boolean isAccessibilityButtonSupported();
+ method public boolean isAudioDescriptionRequested();
method public boolean isEnabled();
method public boolean isTouchExplorationEnabled();
method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
@@ -51422,14 +51481,18 @@
public static final class AccessibilityNodeInfo.CollectionItemInfo {
ctor public AccessibilityNodeInfo.CollectionItemInfo(int, int, int, int, boolean);
ctor public AccessibilityNodeInfo.CollectionItemInfo(int, int, int, int, boolean, boolean);
+ ctor public AccessibilityNodeInfo.CollectionItemInfo(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
method public int getColumnIndex();
method public int getColumnSpan();
+ method @Nullable public String getColumnTitle();
method public int getRowIndex();
method public int getRowSpan();
+ method @Nullable public String getRowTitle();
method @Deprecated public boolean isHeading();
method public boolean isSelected();
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
+ method @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
}
public static final class AccessibilityNodeInfo.ExtraRenderingInfo {
@@ -52485,6 +52548,7 @@
method public boolean hideSoftInputFromWindow(android.os.IBinder, int);
method public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver);
method @Deprecated public void hideStatusIcon(android.os.IBinder);
+ method public void invalidateInput(@NonNull android.view.View);
method public boolean isAcceptingText();
method public boolean isActive(android.view.View);
method public boolean isActive();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 29a4453..fb8e0c7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -215,6 +215,7 @@
field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
+ field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY";
field public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION";
field public static final String READ_ACTIVE_EMERGENCY_SESSION = "android.permission.READ_ACTIVE_EMERGENCY_SESSION";
@@ -256,7 +257,8 @@
field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
field public static final String REQUEST_COMPANION_PROFILE_APP_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING";
field public static final String REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION = "android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION";
- field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
+ field public static final String REQUEST_COMPANION_SELF_MANAGED = "android.permission.REQUEST_COMPANION_SELF_MANAGED";
+ field @Deprecated public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
@@ -267,10 +269,11 @@
field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
field public static final String ROTATE_SURFACE_FLINGER = "android.permission.ROTATE_SURFACE_FLINGER";
field public static final String SCHEDULE_PRIORITIZED_ALARM = "android.permission.SCHEDULE_PRIORITIZED_ALARM";
- field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
+ field @Deprecated public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
field public static final String SEND_CATEGORY_CAR_NOTIFICATIONS = "android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS";
field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
+ field public static final String SEND_SAFETY_CENTER_UPDATE = "android.permission.SEND_SAFETY_CENTER_UPDATE";
field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
field public static final String SERIAL_PORT = "android.permission.SERIAL_PORT";
@@ -726,6 +729,10 @@
field public static final String ACTION_DOWNLOAD_COMPLETED = "android.intent.action.DOWNLOAD_COMPLETED";
}
+ public final class GameManager {
+ method @RequiresPermission("android.permission.MANAGE_GAME_MODE") public void setGameMode(@NonNull String, int);
+ }
+
public abstract class InstantAppResolverService extends android.app.Service {
ctor public InstantAppResolverService();
method public final void attachBaseContext(android.content.Context);
@@ -961,20 +968,20 @@
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getDeviceOwnerNameOnAnyUser();
method @Nullable public CharSequence getDeviceOwnerOrganizationName();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserProvisioningState();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
method public boolean isDeviceManaged();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isManagedKiosk();
method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isUnattendedManagedKiosk();
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
@@ -2000,6 +2007,8 @@
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean removeActiveDevice(int);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setActiveDevice(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int setDiscoverableTimeout(@NonNull java.time.Duration);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int setScanMode(int);
field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
@@ -2016,6 +2025,13 @@
method public void onOobData(int, @NonNull android.bluetooth.OobData);
}
+ public final class BluetoothClass implements android.os.Parcelable {
+ field public static final int PROFILE_A2DP_SINK = 6; // 0x6
+ field public static final int PROFILE_NAP = 5; // 0x5
+ field public static final int PROFILE_OPP = 2; // 0x2
+ field public static final int PROFILE_PANU = 4; // 0x4
+ }
+
public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<java.lang.Integer> getAllGroupIds(@Nullable android.os.ParcelUuid);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
@@ -2343,15 +2359,34 @@
package android.companion {
+ public final class AssociationInfo implements android.os.Parcelable {
+ method @NonNull public String getPackageName();
+ method public boolean isSelfManaged();
+ }
+
public final class AssociationRequest implements android.os.Parcelable {
+ method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public boolean isForceConfirmation();
+ method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public boolean isSelfManaged();
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING) public static final String DEVICE_PROFILE_APP_STREAMING = "android.app.role.COMPANION_DEVICE_APP_STREAMING";
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION) public static final String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION = "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
}
+ public static final class AssociationRequest.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setForceConfirmation(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setSelfManaged(boolean);
+ }
+
public final class CompanionDeviceManager {
+ method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void addOnAssociationsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
method @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public void associate(@NonNull String, @NonNull android.net.MacAddress, @NonNull byte[]);
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public java.util.List<android.companion.AssociationInfo> getAllAssociations();
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
+ method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void removeOnAssociationsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
+ }
+
+ public static interface CompanionDeviceManager.OnAssociationsChangedListener {
+ method public void onAssociationsChanged(@NonNull java.util.List<android.companion.AssociationInfo>);
}
}
@@ -2437,13 +2472,14 @@
field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
field public static final String MUSIC_RECOGNITION_SERVICE = "music_recognition";
field public static final String NETD_SERVICE = "netd";
- field public static final String NETWORK_SCORE_SERVICE = "network_score";
+ field @Deprecated public static final String NETWORK_SCORE_SERVICE = "network_score";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
field public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
field public static final String REBOOT_READINESS_SERVICE = "reboot_readiness";
field public static final String ROLLBACK_SERVICE = "rollback";
+ field public static final String SAFETY_CENTER_SERVICE = "safety_center";
field public static final String SEARCH_UI_SERVICE = "search_ui";
field public static final String SECURE_ELEMENT_SERVICE = "secure_element";
field public static final String SMARTSPACE_SERVICE = "smartspace";
@@ -6308,6 +6344,7 @@
method public int getAudioStreamType();
method public int getVideoStreamType();
method public boolean isPassthrough();
+ method public boolean useSecureMemory();
field public static final int AUDIO_STREAM_TYPE_AAC = 6; // 0x6
field public static final int AUDIO_STREAM_TYPE_AC3 = 7; // 0x7
field public static final int AUDIO_STREAM_TYPE_AC4 = 9; // 0x9
@@ -6343,6 +6380,7 @@
method @NonNull public android.media.tv.tuner.filter.AvSettings build();
method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setAudioStreamType(int);
method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setPassthrough(boolean);
+ method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setUseSecureMemory(boolean);
method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setVideoStreamType(int);
}
@@ -6365,14 +6403,14 @@
}
public class Filter implements java.lang.AutoCloseable {
+ method @Nullable public String acquireSharedFilterToken();
method public void close();
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
- method @Nullable public String createSharedFilter();
method public int flush();
+ method public void freeSharedFilterToken(@NonNull String);
method @Deprecated public int getId();
method public long getIdLong();
method public int read(@NonNull byte[], long, long);
- method public void releaseSharedFilter(@NonNull String);
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
method public int setMonitorEventMask(int);
method public int start();
@@ -6518,6 +6556,7 @@
method public int getTsIndexMask();
field public static final int INDEX_TYPE_NONE = 0; // 0x0
field public static final int INDEX_TYPE_SC = 1; // 0x1
+ field public static final int INDEX_TYPE_SC_AVC = 3; // 0x3
field public static final int INDEX_TYPE_SC_HEVC = 2; // 0x2
field public static final int MPT_INDEX_AUDIO = 262144; // 0x40000
field public static final int MPT_INDEX_MPT = 65536; // 0x10000
@@ -6610,6 +6649,7 @@
method @NonNull public static android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder builder(int);
method public int getTableId();
method public int getVersion();
+ field public static final int INVALID_TABLE_INFO_VERSION = -1; // 0xffffffff
}
public static class SectionSettingsWithTableInfo.Builder extends android.media.tv.tuner.filter.SectionSettings.Builder<android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder> {
@@ -7668,15 +7708,15 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR;
}
- public class NetworkKey implements android.os.Parcelable {
- ctor public NetworkKey(android.net.WifiKey);
- method @Nullable public static android.net.NetworkKey createFromScanResult(@NonNull android.net.wifi.ScanResult);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkKey> CREATOR;
- field public static final int TYPE_WIFI = 1; // 0x1
- field public final int type;
- field public final android.net.WifiKey wifiKey;
+ @Deprecated public class NetworkKey implements android.os.Parcelable {
+ ctor @Deprecated public NetworkKey(android.net.WifiKey);
+ method @Deprecated @Nullable public static android.net.NetworkKey createFromScanResult(@NonNull android.net.wifi.ScanResult);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkKey> CREATOR;
+ field @Deprecated public static final int TYPE_WIFI = 1; // 0x1
+ field @Deprecated public final int type;
+ field @Deprecated public final android.net.WifiKey wifiKey;
}
public abstract class NetworkRecommendationProvider {
@@ -7685,31 +7725,31 @@
method public abstract void onRequestScores(android.net.NetworkKey[]);
}
- public class NetworkScoreManager {
- method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException;
- method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException;
- method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public String getActiveScorerPackage();
- method @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public void registerNetworkScoreCallback(int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.NetworkScoreManager.NetworkScoreCallback) throws java.lang.SecurityException;
- method @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public boolean requestScores(@NonNull java.util.Collection<android.net.NetworkKey>) throws java.lang.SecurityException;
- method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean setActiveScorer(String) throws java.lang.SecurityException;
- method @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS) public boolean updateScores(@NonNull android.net.ScoredNetwork[]) throws java.lang.SecurityException;
+ @Deprecated public class NetworkScoreManager {
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException;
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException;
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public String getActiveScorerPackage();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public void registerNetworkScoreCallback(int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.NetworkScoreManager.NetworkScoreCallback) throws java.lang.SecurityException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public boolean requestScores(@NonNull java.util.Collection<android.net.NetworkKey>) throws java.lang.SecurityException;
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean setActiveScorer(String) throws java.lang.SecurityException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS) public boolean updateScores(@NonNull android.net.ScoredNetwork[]) throws java.lang.SecurityException;
field @Deprecated public static final String ACTION_CHANGE_ACTIVE = "android.net.scoring.CHANGE_ACTIVE";
- field public static final String ACTION_CUSTOM_ENABLE = "android.net.scoring.CUSTOM_ENABLE";
- field public static final String ACTION_RECOMMEND_NETWORKS = "android.net.action.RECOMMEND_NETWORKS";
- field public static final String ACTION_SCORER_CHANGED = "android.net.scoring.SCORER_CHANGED";
+ field @Deprecated public static final String ACTION_CUSTOM_ENABLE = "android.net.scoring.CUSTOM_ENABLE";
+ field @Deprecated public static final String ACTION_RECOMMEND_NETWORKS = "android.net.action.RECOMMEND_NETWORKS";
+ field @Deprecated public static final String ACTION_SCORER_CHANGED = "android.net.scoring.SCORER_CHANGED";
field @Deprecated public static final String ACTION_SCORE_NETWORKS = "android.net.scoring.SCORE_NETWORKS";
field @Deprecated public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
- field public static final String EXTRA_NEW_SCORER = "newScorer";
+ field @Deprecated public static final String EXTRA_NEW_SCORER = "newScorer";
field @Deprecated public static final String EXTRA_PACKAGE_NAME = "packageName";
- field public static final int SCORE_FILTER_CURRENT_NETWORK = 1; // 0x1
- field public static final int SCORE_FILTER_NONE = 0; // 0x0
- field public static final int SCORE_FILTER_SCAN_RESULTS = 2; // 0x2
+ field @Deprecated public static final int SCORE_FILTER_CURRENT_NETWORK = 1; // 0x1
+ field @Deprecated public static final int SCORE_FILTER_NONE = 0; // 0x0
+ field @Deprecated public static final int SCORE_FILTER_SCAN_RESULTS = 2; // 0x2
}
- public abstract static class NetworkScoreManager.NetworkScoreCallback {
- ctor public NetworkScoreManager.NetworkScoreCallback();
- method public abstract void onScoresInvalidated();
- method public abstract void onScoresUpdated(@NonNull java.util.Collection<android.net.ScoredNetwork>);
+ @Deprecated public abstract static class NetworkScoreManager.NetworkScoreCallback {
+ ctor @Deprecated public NetworkScoreManager.NetworkScoreCallback();
+ method @Deprecated public abstract void onScoresInvalidated();
+ method @Deprecated public abstract void onScoresUpdated(@NonNull java.util.Collection<android.net.ScoredNetwork>);
}
public abstract class NetworkSpecifier {
@@ -7748,35 +7788,35 @@
ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
}
- public class RssiCurve implements android.os.Parcelable {
- ctor public RssiCurve(int, int, byte[]);
- ctor public RssiCurve(int, int, byte[], int);
- method public int describeContents();
- method public byte lookupScore(int);
- method public byte lookupScore(int, boolean);
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.RssiCurve> CREATOR;
- field public final int activeNetworkRssiBoost;
- field public final int bucketWidth;
- field public final byte[] rssiBuckets;
- field public final int start;
+ @Deprecated public class RssiCurve implements android.os.Parcelable {
+ ctor @Deprecated public RssiCurve(int, int, byte[]);
+ ctor @Deprecated public RssiCurve(int, int, byte[], int);
+ method @Deprecated public int describeContents();
+ method @Deprecated public byte lookupScore(int);
+ method @Deprecated public byte lookupScore(int, boolean);
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.RssiCurve> CREATOR;
+ field @Deprecated public final int activeNetworkRssiBoost;
+ field @Deprecated public final int bucketWidth;
+ field @Deprecated public final byte[] rssiBuckets;
+ field @Deprecated public final int start;
}
- public class ScoredNetwork implements android.os.Parcelable {
- ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve);
- ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean);
- ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, @Nullable android.os.Bundle);
- method public int calculateBadge(int);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final String ATTRIBUTES_KEY_BADGING_CURVE = "android.net.attributes.key.BADGING_CURVE";
- field public static final String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
- field public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = "android.net.attributes.key.RANKING_SCORE_OFFSET";
- field @NonNull public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
- field @Nullable public final android.os.Bundle attributes;
- field public final boolean meteredHint;
- field public final android.net.NetworkKey networkKey;
- field public final android.net.RssiCurve rssiCurve;
+ @Deprecated public class ScoredNetwork implements android.os.Parcelable {
+ ctor @Deprecated public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve);
+ ctor @Deprecated public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean);
+ ctor @Deprecated public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, @Nullable android.os.Bundle);
+ method @Deprecated public int calculateBadge(int);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated public static final String ATTRIBUTES_KEY_BADGING_CURVE = "android.net.attributes.key.BADGING_CURVE";
+ field @Deprecated public static final String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
+ field @Deprecated public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = "android.net.attributes.key.RANKING_SCORE_OFFSET";
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
+ field @Deprecated @Nullable public final android.os.Bundle attributes;
+ field @Deprecated public final boolean meteredHint;
+ field @Deprecated public final android.net.NetworkKey networkKey;
+ field @Deprecated public final android.net.RssiCurve rssiCurve;
}
public class TrafficStats {
@@ -7803,13 +7843,13 @@
ctor public WebAddress(String) throws android.net.ParseException;
}
- public class WifiKey implements android.os.Parcelable {
- ctor public WifiKey(String, String);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.WifiKey> CREATOR;
- field public final String bssid;
- field public final String ssid;
+ @Deprecated public class WifiKey implements android.os.Parcelable {
+ ctor @Deprecated public WifiKey(String, String);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.WifiKey> CREATOR;
+ field @Deprecated public final String bssid;
+ field @Deprecated public final String ssid;
}
}
@@ -8681,7 +8721,11 @@
}
public final class NewUserRequest {
+ method @Nullable public String getAccountName();
+ method @Nullable public android.os.PersistableBundle getAccountOptions();
+ method @Nullable public String getAccountType();
method @Nullable public String getName();
+ method @Nullable public android.graphics.Bitmap getUserIcon();
method @NonNull public String getUserType();
method public boolean isAdmin();
method public boolean isEphemeral();
@@ -8690,9 +8734,13 @@
public static final class NewUserRequest.Builder {
ctor public NewUserRequest.Builder();
method @NonNull public android.os.NewUserRequest build();
+ method @NonNull public android.os.NewUserRequest.Builder setAccountName(@Nullable String);
+ method @NonNull public android.os.NewUserRequest.Builder setAccountOptions(@Nullable android.os.PersistableBundle);
+ method @NonNull public android.os.NewUserRequest.Builder setAccountType(@Nullable String);
method @NonNull public android.os.NewUserRequest.Builder setAdmin();
method @NonNull public android.os.NewUserRequest.Builder setEphemeral();
method @NonNull public android.os.NewUserRequest.Builder setName(@Nullable String);
+ method @NonNull public android.os.NewUserRequest.Builder setUserIcon(@Nullable android.graphics.Bitmap);
method @NonNull public android.os.NewUserRequest.Builder setUserType(@NonNull String);
}
@@ -8970,6 +9018,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String);
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -8981,6 +9030,7 @@
field public static final int SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED = 4; // 0x4
field public static final int SWITCHABILITY_STATUS_USER_IN_CALL = 1; // 0x1
field public static final int SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED = 2; // 0x2
+ field public static final int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7; // 0x7
field public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
field public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
@@ -12302,7 +12352,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
method public int getMaxNumberOfSimultaneouslyActiveSims();
method public static long getMaxNumberVerificationTimeoutMillis();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup();
@@ -12310,10 +12360,13 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
method public int getSimApplicationState();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimApplicationState(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimApplicationState(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimApplicationState(int, int);
method public int getSimCardState();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Collection<android.telephony.UiccSlotMapping> getSimSlotMapping();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telephony.RadioAccessSpecifier> getSystemSelectionChannels();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -13164,6 +13217,7 @@
method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteredFeatureTags();
method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteringFeatureTags();
method @NonNull public java.util.Set<java.lang.String> getRegisteredFeatureTags();
+ method @NonNull public java.util.Set<java.lang.String> getRegisteringFeatureTags();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRegistrationState> CREATOR;
field public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1; // 0x1
@@ -13181,6 +13235,7 @@
method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteringFeatureTag(@NonNull String, int);
method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTag(@NonNull String);
method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTags(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteringFeatureTags(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.telephony.ims.DelegateRegistrationState build();
}
@@ -14696,6 +14751,10 @@
ctor public TranslationCapability(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, boolean, int);
}
+ public final class TranslationContext implements android.os.Parcelable {
+ method @Nullable public android.app.assist.ActivityId getActivityId();
+ }
+
public final class UiTranslationManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(@NonNull android.app.assist.ActivityId);
method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void pauseTranslation(@NonNull android.app.assist.ActivityId);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f83b3a4..c1ab070 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -267,10 +267,6 @@
method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream();
}
- public final class GameManager {
- method @RequiresPermission("android.permission.MANAGE_GAME_MODE") public void setGameMode(@NonNull String, int);
- }
-
public abstract class HomeVisibilityListener {
ctor public HomeVisibilityListener();
method public abstract void onHomeVisibilityChanged(boolean);
@@ -1458,6 +1454,7 @@
method public boolean hasRegisteredDynamicPolicy();
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int);
+ method public void setRampingRingerEnabled(boolean);
}
public static final class AudioRecord.MetricsConstants {
@@ -1834,11 +1831,6 @@
method public int getAudioUsage();
}
- public static final class VibrationAttributes.Builder {
- ctor public VibrationAttributes.Builder(@NonNull android.media.AudioAttributes, @NonNull android.os.VibrationEffect);
- ctor public VibrationAttributes.Builder(@NonNull android.os.VibrationAttributes, @NonNull android.os.VibrationEffect);
- }
-
public abstract class VibrationEffect implements android.os.Parcelable {
method public static android.os.VibrationEffect get(int);
method public static android.os.VibrationEffect get(int, boolean);
@@ -1855,13 +1847,9 @@
}
public static final class VibrationEffect.Composed extends android.os.VibrationEffect {
- method @NonNull public android.os.VibrationEffect.Composed applyEffectStrength(int);
method public long getDuration();
method public int getRepeatIndex();
method @NonNull public java.util.List<android.os.vibrator.VibrationEffectSegment> getSegments();
- method @NonNull public android.os.VibrationEffect.Composed resolve(int);
- method @NonNull public android.os.VibrationEffect.Composed scale(float);
- method public void validate();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Composed> CREATOR;
}
@@ -2009,72 +1997,47 @@
package android.os.vibrator {
public final class PrebakedSegment extends android.os.vibrator.VibrationEffectSegment {
- method @NonNull public android.os.vibrator.PrebakedSegment applyEffectStrength(int);
method public int describeContents();
method public long getDuration();
method public int getEffectId();
method public int getEffectStrength();
- method public boolean hasNonZeroAmplitude();
- method @NonNull public android.os.vibrator.PrebakedSegment resolve(int);
- method @NonNull public android.os.vibrator.PrebakedSegment scale(float);
method public boolean shouldFallback();
- method public void validate();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.PrebakedSegment> CREATOR;
}
public final class PrimitiveSegment extends android.os.vibrator.VibrationEffectSegment {
- method @NonNull public android.os.vibrator.PrimitiveSegment applyEffectStrength(int);
method public int describeContents();
method public int getDelay();
method public long getDuration();
method public int getPrimitiveId();
method public float getScale();
- method public boolean hasNonZeroAmplitude();
- method @NonNull public android.os.vibrator.PrimitiveSegment resolve(int);
- method @NonNull public android.os.vibrator.PrimitiveSegment scale(float);
- method public void validate();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.PrimitiveSegment> CREATOR;
}
public final class RampSegment extends android.os.vibrator.VibrationEffectSegment {
- method @NonNull public android.os.vibrator.RampSegment applyEffectStrength(int);
method public int describeContents();
method public long getDuration();
method public float getEndAmplitude();
method public float getEndFrequency();
method public float getStartAmplitude();
method public float getStartFrequency();
- method public boolean hasNonZeroAmplitude();
- method @NonNull public android.os.vibrator.RampSegment resolve(int);
- method @NonNull public android.os.vibrator.RampSegment scale(float);
- method public void validate();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.RampSegment> CREATOR;
}
public final class StepSegment extends android.os.vibrator.VibrationEffectSegment {
- method @NonNull public android.os.vibrator.StepSegment applyEffectStrength(int);
method public int describeContents();
method public float getAmplitude();
method public long getDuration();
method public float getFrequency();
- method public boolean hasNonZeroAmplitude();
- method @NonNull public android.os.vibrator.StepSegment resolve(int);
- method @NonNull public android.os.vibrator.StepSegment scale(float);
- method public void validate();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.StepSegment> CREATOR;
}
public abstract class VibrationEffectSegment implements android.os.Parcelable {
- method @NonNull public abstract <T extends android.os.vibrator.VibrationEffectSegment> T applyEffectStrength(int);
method public abstract long getDuration();
- method public abstract boolean hasNonZeroAmplitude();
- method @NonNull public abstract <T extends android.os.vibrator.VibrationEffectSegment> T resolve(int);
- method @NonNull public abstract <T extends android.os.vibrator.VibrationEffectSegment> T scale(float);
- method public abstract void validate();
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.VibrationEffectSegment> CREATOR;
}
@@ -2194,7 +2157,7 @@
field public static final String USER_PREFERRED_REFRESH_RATE = "user_preferred_refresh_rate";
field public static final String USER_PREFERRED_RESOLUTION_HEIGHT = "user_preferred_resolution_height";
field public static final String USER_PREFERRED_RESOLUTION_WIDTH = "user_preferred_resolution_width";
- field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
+ field @Deprecated public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
@@ -3250,12 +3213,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR;
}
- public final class TaskFragmentAppearedInfo implements android.os.Parcelable {
- method @NonNull public android.view.SurfaceControl getLeash();
- method @NonNull public android.window.TaskFragmentInfo getTaskFragmentInfo();
- field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentAppearedInfo> CREATOR;
- }
-
public final class TaskFragmentCreationParams implements android.os.Parcelable {
method @NonNull public android.os.IBinder getFragmentToken();
method @NonNull public android.graphics.Rect getInitialBounds();
@@ -3292,7 +3249,7 @@
ctor public TaskFragmentOrganizer(@NonNull java.util.concurrent.Executor);
method @NonNull public java.util.concurrent.Executor getExecutor();
method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizerToken();
- method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentAppearedInfo);
+ method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentInfo);
method public void onTaskFragmentError(@NonNull android.os.IBinder, @NonNull Throwable);
method public void onTaskFragmentInfoChanged(@NonNull android.window.TaskFragmentInfo);
method public void onTaskFragmentParentInfoChanged(@NonNull android.os.IBinder, @NonNull android.content.res.Configuration);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 0f852b4..09af72d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -2071,7 +2071,7 @@
try {
connection.setServiceInfo(mInfo);
mInfo = null;
- AccessibilityInteractionClient.getInstance(this).clearCache();
+ AccessibilityInteractionClient.getInstance(this).clearCache(mConnectionId);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
re.rethrowFromSystemServer();
@@ -2421,7 +2421,7 @@
if (event != null) {
// Send the event to AccessibilityCache via AccessibilityInteractionClient
AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
- event);
+ event, mConnectionId);
if (serviceWantsEvent
&& (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
// Send the event to AccessibilityService
@@ -2451,7 +2451,7 @@
args.recycle();
if (connection != null) {
AccessibilityInteractionClient.getInstance(mContext).addConnection(
- mConnectionId, connection);
+ mConnectionId, connection, /*initializeCache=*/true);
if (mContext != null) {
try {
connection.setAttributionTag(mContext.getAttributionTag());
@@ -2466,7 +2466,8 @@
AccessibilityInteractionClient.getInstance(mContext).removeConnection(
mConnectionId);
mConnectionId = AccessibilityInteractionClient.NO_ID;
- AccessibilityInteractionClient.getInstance(mContext).clearCache();
+ AccessibilityInteractionClient.getInstance(mContext)
+ .clearCache(mConnectionId);
mCallback.init(AccessibilityInteractionClient.NO_ID, null);
}
return;
@@ -2478,7 +2479,7 @@
return;
}
case DO_CLEAR_ACCESSIBILITY_CACHE: {
- AccessibilityInteractionClient.getInstance(mContext).clearCache();
+ AccessibilityInteractionClient.getInstance(mContext).clearCache(mConnectionId);
return;
}
case DO_ON_KEY_EVENT: {
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/accessibilityservice/MagnificationConfig.aidl
similarity index 77%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to core/java/android/accessibilityservice/MagnificationConfig.aidl
index 3729c09..fe415a8 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/java/android/accessibilityservice/MagnificationConfig.aidl
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package android.window;
+package android.accessibilityservice;
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+parcelable MagnificationConfig;
diff --git a/core/java/android/accessibilityservice/MagnificationConfig.java b/core/java/android/accessibilityservice/MagnificationConfig.java
new file mode 100644
index 0000000..8884508
--- /dev/null
+++ b/core/java/android/accessibilityservice/MagnificationConfig.java
@@ -0,0 +1,250 @@
+/*
+ * 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 android.accessibilityservice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class describes the magnification config for {@link AccessibilityService} to control the
+ * magnification.
+ *
+ * <p>
+ * When the magnification config uses {@link #DEFAULT_MODE},
+ * {@link AccessibilityService} will be able to control the activated magnifier on the display.
+ * If there is no magnifier activated, it controls the last-activated magnification mode.
+ * If there is no magnifier activated before, it controls full-screen magnifier by default.
+ * </p>
+ *
+ * <p>
+ * When the magnification config uses {@link #FULLSCREEN_MODE}. {@link AccessibilityService} will
+ * be able to control full-screen magnifier on the display.
+ * </p>
+ *
+ * <p>
+ * When the magnification config uses {@link #WINDOW_MODE}. {@link AccessibilityService} will be
+ * able to control the activated window magnifier on the display.
+ * </p>
+ *
+ * <p>
+ * If the other magnification configs, scale centerX and centerY, are not set by the
+ * {@link Builder}, the configs should be current values or default values. And the center
+ * position ordinarily is the center of the screen.
+ * </p>
+ */
+public final class MagnificationConfig implements Parcelable {
+
+ /** The controlling magnification mode. It controls the activated magnifier. */
+ public static final int DEFAULT_MODE = 0;
+ /** The controlling magnification mode. It controls fullscreen magnifier. */
+ public static final int FULLSCREEN_MODE = 1;
+ /** The controlling magnification mode. It controls window magnifier. */
+ public static final int WINDOW_MODE = 2;
+
+ @IntDef(prefix = {"MAGNIFICATION_MODE"}, value = {
+ DEFAULT_MODE,
+ FULLSCREEN_MODE,
+ WINDOW_MODE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface MAGNIFICATION_MODE {
+ }
+
+ private int mMode = DEFAULT_MODE;
+ private float mScale = Float.NaN;
+ private float mCenterX = Float.NaN;
+ private float mCenterY = Float.NaN;
+
+ private MagnificationConfig() {
+ /* do nothing */
+ }
+
+ private MagnificationConfig(@NonNull Parcel parcel) {
+ mMode = parcel.readInt();
+ mScale = parcel.readFloat();
+ mCenterX = parcel.readFloat();
+ mCenterY = parcel.readFloat();
+ }
+
+ /**
+ * Returns the magnification mode that is the current activated mode or the controlling mode of
+ * the config.
+ *
+ * @return The magnification mode
+ */
+ public int getMode() {
+ return mMode;
+ }
+
+ /**
+ * Returns the magnification scale of the controlling magnifier
+ *
+ * @return the scale If the controlling magnifier is not activated, it returns 1 by default
+ */
+ public float getScale() {
+ return mScale;
+ }
+
+ /**
+ * Returns the screen-relative X coordinate of the center of the magnification viewport.
+ *
+ * @return the X coordinate. If the controlling magnifier is {@link #WINDOW_MODE} but not
+ * enabled, it returns {@link Float#NaN}. If the controlling magnifier is {@link
+ * #FULLSCREEN_MODE} but not enabled, it returns 0
+ */
+ public float getCenterX() {
+ return mCenterX;
+ }
+
+ /**
+ * Returns the screen-relative Y coordinate of the center of the magnification viewport.
+ *
+ * @return the Y coordinate If the controlling magnifier is {@link #WINDOW_MODE} but not
+ * enabled, it returns {@link Float#NaN}. If the controlling magnifier is {@link
+ * #FULLSCREEN_MODE} but not enabled, it returns 0
+ */
+ public float getCenterY() {
+ return mCenterY;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder("MagnificationConfig[");
+ stringBuilder.append("mode: ").append(getMode());
+ stringBuilder.append(", ");
+ stringBuilder.append("scale: ").append(getScale());
+ stringBuilder.append(", ");
+ stringBuilder.append("centerX: ").append(getCenterX());
+ stringBuilder.append(", ");
+ stringBuilder.append("centerY: ").append(getCenterY());
+ stringBuilder.append("] ");
+ return stringBuilder.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mMode);
+ parcel.writeFloat(mScale);
+ parcel.writeFloat(mCenterX);
+ parcel.writeFloat(mCenterY);
+ }
+
+ /**
+ * Builder for creating {@link MagnificationConfig} objects.
+ */
+ public static final class Builder {
+
+ private int mMode = DEFAULT_MODE;
+ private float mScale = Float.NaN;
+ private float mCenterX = Float.NaN;
+ private float mCenterY = Float.NaN;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Sets the magnification mode.
+ *
+ * @param mode The magnification mode
+ * @return This builder
+ */
+ @NonNull
+ public MagnificationConfig.Builder setMode(@MAGNIFICATION_MODE int mode) {
+ mMode = mode;
+ return this;
+ }
+
+ /**
+ * Sets the magnification scale.
+ *
+ * @param scale The magnification scale
+ * @return This builder
+ */
+ @NonNull
+ public MagnificationConfig.Builder setScale(float scale) {
+ mScale = scale;
+ return this;
+ }
+
+ /**
+ * Sets the X coordinate of the center of the magnification viewport.
+ *
+ * @param centerX the screen-relative X coordinate around which to
+ * center and scale, or {@link Float#NaN} to leave unchanged
+ * @return This builder
+ */
+ @NonNull
+ public MagnificationConfig.Builder setCenterX(float centerX) {
+ mCenterX = centerX;
+ return this;
+ }
+
+ /**
+ * Sets the Y coordinate of the center of the magnification viewport.
+ *
+ * @param centerY the screen-relative Y coordinate around which to
+ * center and scale, or {@link Float#NaN} to leave unchanged
+ * @return This builder
+ */
+ @NonNull
+ public MagnificationConfig.Builder setCenterY(float centerY) {
+ mCenterY = centerY;
+ return this;
+ }
+
+ /**
+ * Builds and returns a {@link MagnificationConfig}
+ */
+ @NonNull
+ public MagnificationConfig build() {
+ MagnificationConfig magnificationConfig = new MagnificationConfig();
+ magnificationConfig.mMode = mMode;
+ magnificationConfig.mScale = mScale;
+ magnificationConfig.mCenterX = mCenterX;
+ magnificationConfig.mCenterY = mCenterY;
+ return magnificationConfig;
+ }
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ public static final @NonNull Parcelable.Creator<MagnificationConfig> CREATOR =
+ new Parcelable.Creator<MagnificationConfig>() {
+ public MagnificationConfig createFromParcel(Parcel parcel) {
+ return new MagnificationConfig(parcel);
+ }
+
+ public MagnificationConfig[] newArray(int size) {
+ return new MagnificationConfig[size];
+ }
+ };
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f9cc323..9f8d246 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4082,6 +4082,34 @@
}
/**
+ * Gets the message that is shown when a user is switched from.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public @Nullable String getSwitchingFromUserMessage() {
+ try {
+ return getService().getSwitchingFromUserMessage();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the message that is shown when a user is switched to.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public @Nullable String getSwitchingToUserMessage() {
+ try {
+ return getService().getSwitchingToUserMessage();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Uses the value defined by the platform.
*
* @hide
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index a5facd9..d0096fd 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -431,7 +431,7 @@
private boolean mOverrideTaskTransition;
private String mSplashScreenThemeResName;
@SplashScreen.SplashScreenStyle
- private int mSplashScreenStyle;
+ private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
private boolean mRemoveWithTaskOrganizer;
private boolean mLaunchedFromBubble;
private boolean mTransientLaunch;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c89e8b0..15f67d0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -29,6 +29,7 @@
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
@@ -322,7 +323,7 @@
@UnsupportedAppUsage
private ContextImpl mSystemContext;
- private ContextImpl mSystemUiContext;
+ private final SparseArray<ContextImpl> mDisplaySystemUiContexts = new SparseArray<>();
@UnsupportedAppUsage
static volatile IPackageManager sPackageManager;
@@ -1301,8 +1302,11 @@
}
@Override
- public void scheduleCrash(String msg, int typeId) {
- sendMessage(H.SCHEDULE_CRASH, msg, typeId);
+ public void scheduleCrash(String msg, int typeId, @Nullable Bundle extras) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = msg;
+ args.arg2 = extras;
+ sendMessage(H.SCHEDULE_CRASH, args, typeId);
}
public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken,
@@ -1932,11 +1936,11 @@
}
}
- private void throwRemoteServiceException(String message, int typeId) {
+ private void throwRemoteServiceException(String message, int typeId, @Nullable Bundle extras) {
// Use a switch to ensure all the type IDs are unique.
switch (typeId) {
case ForegroundServiceDidNotStartInTimeException.TYPE_ID:
- throw new ForegroundServiceDidNotStartInTimeException(message);
+ throw generateForegroundServiceDidNotStartInTimeException(message, extras);
case CannotDeliverBroadcastException.TYPE_ID:
throw new CannotDeliverBroadcastException(message);
@@ -1959,6 +1963,15 @@
}
}
+ private ForegroundServiceDidNotStartInTimeException
+ generateForegroundServiceDidNotStartInTimeException(String message, Bundle extras) {
+ final String serviceClassName =
+ ForegroundServiceDidNotStartInTimeException.getServiceClassNameFromExtras(extras);
+ final Exception inner = (serviceClassName == null) ? null
+ : Service.getStartForegroundServiceStackTrace(serviceClassName);
+ throw new ForegroundServiceDidNotStartInTimeException(message, inner);
+ }
+
class H extends Handler {
public static final int BIND_APPLICATION = 110;
@UnsupportedAppUsage
@@ -2176,9 +2189,14 @@
handleDispatchPackageBroadcast(msg.arg1, (String[])msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
- case SCHEDULE_CRASH:
- throwRemoteServiceException((String) msg.obj, msg.arg1);
+ case SCHEDULE_CRASH: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String message = (String) args.arg1;
+ Bundle extras = (Bundle) args.arg2;
+ args.recycle();
+ throwRemoteServiceException(message, msg.arg1, extras);
break;
+ }
case DUMP_HEAP:
handleDumpHeap((DumpHeapData) msg.obj);
break;
@@ -2641,22 +2659,26 @@
}
@Override
+ @NonNull
public ContextImpl getSystemUiContext() {
- synchronized (this) {
- if (mSystemUiContext == null) {
- mSystemUiContext = ContextImpl.createSystemUiContext(getSystemContext());
- }
- return mSystemUiContext;
- }
+ return getSystemUiContext(DEFAULT_DISPLAY);
}
/**
- * Create the context instance base on system resources & display information which used for UI.
+ * Gets the context instance base on system resources & display information which used for UI.
* @param displayId The ID of the display where the UI is shown.
* @see ContextImpl#createSystemUiContext(ContextImpl, int)
*/
- public ContextImpl createSystemUiContext(int displayId) {
- return ContextImpl.createSystemUiContext(getSystemUiContext(), displayId);
+ @NonNull
+ public ContextImpl getSystemUiContext(int displayId) {
+ synchronized (this) {
+ ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId);
+ if (systemUiContext == null) {
+ systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId);
+ mDisplaySystemUiContexts.put(displayId, systemUiContext);
+ }
+ return systemUiContext;
+ }
}
public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
@@ -3775,7 +3797,7 @@
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
- if (id != Display.DEFAULT_DISPLAY) {
+ if (id != DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7a545f6..565f690 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -9469,23 +9469,23 @@
e.rethrowFromSystemServer();
}
- if (missedAsyncOps != null) {
+ // Copy pointer so callback can be dispatched out of lock
+ OnOpNotedCallback onOpNotedCallback = sOnOpNotedCallback;
+ if (onOpNotedCallback != null && missedAsyncOps != null) {
int numMissedAsyncOps = missedAsyncOps.size();
for (int i = 0; i < numMissedAsyncOps; i++) {
final AsyncNotedAppOp asyncNotedAppOp = missedAsyncOps.get(i);
- if (sOnOpNotedCallback != null) {
- sOnOpNotedCallback.getAsyncNotedExecutor().execute(
- () -> sOnOpNotedCallback.onAsyncNoted(asyncNotedAppOp));
- }
+ onOpNotedCallback.getAsyncNotedExecutor().execute(
+ () -> onOpNotedCallback.onAsyncNoted(asyncNotedAppOp));
}
}
synchronized (this) {
int numMissedSyncOps = sUnforwardedOps.size();
- for (int i = 0; i < numMissedSyncOps; i++) {
- final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i);
- if (sOnOpNotedCallback != null) {
- sOnOpNotedCallback.getAsyncNotedExecutor().execute(
- () -> sOnOpNotedCallback.onAsyncNoted(syncNotedAppOp));
+ if (onOpNotedCallback != null) {
+ for (int i = 0; i < numMissedSyncOps; i++) {
+ final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i);
+ onOpNotedCallback.getAsyncNotedExecutor().execute(
+ () -> onOpNotedCallback.onAsyncNoted(syncNotedAppOp));
}
}
sUnforwardedOps.clear();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1943f9d..d1b7145 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -920,16 +920,16 @@
Objects.requireNonNull(packageName);
Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(trustedInstallers);
+ if (trustedInstallers == TRUST_ALL) {
+ trustedInstallers = null;
+ } else if (trustedInstallers == TRUST_NONE) {
+ trustedInstallers = Collections.emptyList();
+ } else if (trustedInstallers.isEmpty()) {
+ throw new IllegalArgumentException(
+ "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+ + "list of certificates.");
+ }
try {
- if (trustedInstallers == TRUST_ALL) {
- trustedInstallers = null;
- } else if (trustedInstallers == TRUST_NONE) {
- trustedInstallers = Collections.emptyList();
- } else if (trustedInstallers.isEmpty()) {
- throw new IllegalArgumentException(
- "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
- + "list of certificates.");
- }
IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
new IOnChecksumsReadyListener.Stub() {
@Override
@@ -938,7 +938,7 @@
onChecksumsReadyListener.onChecksumsReady(checksums);
}
};
- mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
+ mPM.requestPackageChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
getUserId());
} catch (ParcelableException e) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 756833a..4a7361e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1882,6 +1882,14 @@
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
+ // If we started a foreground service in the same package, remember the stack trace.
+ if (cn != null && requireForeground) {
+ if (cn.getPackageName().equals(getOpPackageName())) {
+ Service.setStartForegroundServiceStackTrace(cn.getClassName(),
+ new StackTrace("Last startServiceCommon() call for this service was "
+ + "made here"));
+ }
+ }
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2638,7 +2646,10 @@
overrideConfig, display.getDisplayAdjustments().getCompatibilityInfo(),
mResources.getLoaders()));
context.mDisplay = display;
- context.mContextType = CONTEXT_TYPE_DISPLAY_CONTEXT;
+ // Inherit context type if the container is from System or System UI context to bypass
+ // UI context check.
+ context.mContextType = mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
+ ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI : CONTEXT_TYPE_DISPLAY_CONTEXT;
// Display contexts and any context derived from a display context should always override
// the display that would otherwise be inherited from mToken (or the global configuration if
// mToken is null).
@@ -2691,7 +2702,8 @@
// Step 2. Create the base context of the window context, it will also create a Resources
// associated with the WindowTokenClient and set the token to the base context.
- final ContextImpl windowContextBase = createWindowContextBase(windowTokenClient, display);
+ final ContextImpl windowContextBase = createWindowContextBase(windowTokenClient,
+ display.getDisplayId());
// Step 3. Create a WindowContext instance and set it as the outer context of the base
// context to make the service obtained by #getSystemService(String) able to query
@@ -2716,9 +2728,7 @@
if (display == null) {
throw new IllegalArgumentException("Display must not be null");
}
- final ContextImpl tokenContext = createWindowContextBase(token, display);
- tokenContext.setResources(createWindowContextResources(tokenContext));
- return tokenContext;
+ return createWindowContextBase(token, display.getDisplayId());
}
/**
@@ -2726,13 +2736,13 @@
* window.
*
* @param token The token to associate with {@link Resources}
- * @param display The {@link Display} to associate with.
+ * @param displayId The ID of {@link Display} to associate with.
*
* @see #createWindowContext(Display, int, Bundle)
* @see #createTokenContext(IBinder, Display)
*/
@UiContext
- ContextImpl createWindowContextBase(@NonNull IBinder token, @NonNull Display display) {
+ ContextImpl createWindowContextBase(@NonNull IBinder token, int displayId) {
ContextImpl baseContext = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
mAttributionSource.getAttributionTag(),
mAttributionSource.getNext(),
@@ -2746,8 +2756,8 @@
baseContext.setResources(windowContextResources);
// Associate the display with window context resources so that configuration update from
// the server side will also apply to the display's metrics.
- baseContext.mDisplay = ResourcesManager.getInstance()
- .getAdjustedDisplay(display.getDisplayId(), windowContextResources);
+ baseContext.mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(displayId,
+ windowContextResources);
return baseContext;
}
@@ -2983,6 +2993,18 @@
mContentCaptureOptions = options;
}
+ @Override
+ protected void finalize() throws Throwable {
+ // If mToken is a WindowTokenClient, the Context is usually associated with a
+ // WindowContainer. We should detach from WindowContainer when the Context is finalized
+ // if this Context is not a WindowContext. WindowContext finalization is handled in
+ // WindowContext class.
+ if (mToken instanceof WindowTokenClient && mContextType != CONTEXT_TYPE_WINDOW_CONTEXT) {
+ ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
+ }
+ super.finalize();
+ }
+
@UnsupportedAppUsage
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
@@ -3003,22 +3025,13 @@
* @param displayId The ID of the display where the UI is shown.
*/
static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
- final LoadedApk packageInfo = systemContext.mPackageInfo;
- ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, null, null, 0, null, null);
- context.setResources(createResources(null, packageInfo, null, displayId, null,
- packageInfo.getCompatibilityInfo(), null));
- context.updateDisplay(displayId);
+ final WindowTokenClient token = new WindowTokenClient();
+ final ContextImpl context = systemContext.createWindowContextBase(token, displayId);
+ token.attachContext(context);
+ token.attachToDisplayContent(displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
- return context;
- }
- /**
- * The overloaded method of {@link #createSystemUiContext(ContextImpl, int)}.
- * Uses {@Code Display.DEFAULT_DISPLAY} as the target display.
- */
- static ContextImpl createSystemUiContext(ContextImpl systemContext) {
- return createSystemUiContext(systemContext, Display.DEFAULT_DISPLAY);
+ return context;
}
@UnsupportedAppUsage
@@ -3227,7 +3240,13 @@
@Override
public IBinder getWindowContextToken() {
- return mContextType == CONTEXT_TYPE_WINDOW_CONTEXT ? mToken : null;
+ switch (mContextType) {
+ case CONTEXT_TYPE_WINDOW_CONTEXT:
+ case CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI:
+ return mToken;
+ default:
+ return null;
+ }
}
private void checkMode(int mode) {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 9833ed6..3060353 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -151,6 +151,9 @@
private final Runnable mDismissAction = this::dismissDialog;
+ /** A {@link Runnable} to run instead of dismissing when {@link #dismiss()} is called. */
+ private Runnable mDismissOverride;
+
/**
* Creates a dialog window that uses the default dialog theme.
* <p>
@@ -370,6 +373,11 @@
*/
@Override
public void dismiss() {
+ if (mDismissOverride != null) {
+ mDismissOverride.run();
+ return;
+ }
+
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
@@ -1354,6 +1362,21 @@
mDismissMessage = msg;
}
+ /**
+ * Set a {@link Runnable} to run when this dialog is dismissed instead of directly dismissing
+ * it. This allows to animate the dialog in its window before dismissing it.
+ *
+ * Note that {@code override} should always end up calling this method with {@code null}
+ * followed by a call to {@link #dismiss() dismiss} to actually dismiss the dialog.
+ *
+ * @see #dismiss()
+ *
+ * @hide
+ */
+ public void setDismissOverride(@Nullable Runnable override) {
+ mDismissOverride = override;
+ }
+
/** @hide */
public boolean takeCancelAndDismissListeners(@Nullable String msg,
@Nullable OnCancelListener cancel, @Nullable OnDismissListener dismiss) {
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index b324fb6..78759db 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -21,8 +21,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.annotation.UserHandleAware;
import android.content.Context;
import android.os.Handler;
@@ -125,7 +125,7 @@
*
* @hide
*/
- @TestApi
+ @SystemApi
@UserHandleAware
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public void setGameMode(@NonNull String packageName, @GameMode int gameMode) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 8bb4059..8f904b5 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -332,6 +332,8 @@
boolean isTopActivityImmersive();
void crashApplicationWithType(int uid, int initialPid, in String packageName, int userId,
in String message, boolean force, int exceptionTypeId);
+ void crashApplicationWithTypeWithExtras(int uid, int initialPid, in String packageName,
+ int userId, in String message, boolean force, int exceptionTypeId, in Bundle extras);
/** @deprecated -- use getProviderMimeTypeAsync */
@UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives =
"Use {@link android.content.ContentResolver#getType} public API instead.")
@@ -349,6 +351,8 @@
void setPackageScreenCompatMode(in String packageName, int mode);
@UnsupportedAppUsage
boolean switchUser(int userid);
+ String getSwitchingFromUserMessage();
+ String getSwitchingToUserMessage();
@UnsupportedAppUsage
void setStopUserOnSwitch(int value);
boolean removeTask(int taskId);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 0e42a79..1714229 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -108,7 +108,7 @@
void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
void scheduleSuicide();
void dispatchPackageBroadcast(int cmd, in String[] packages);
- void scheduleCrash(in String msg, int typeId);
+ void scheduleCrash(in String msg, int typeId, in Bundle extras);
void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path,
in ParcelFileDescriptor fd, in RemoteCallback finishCallback);
void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
diff --git a/core/java/android/app/RemoteServiceException.java b/core/java/android/app/RemoteServiceException.java
index 1038530..e220627 100644
--- a/core/java/android/app/RemoteServiceException.java
+++ b/core/java/android/app/RemoteServiceException.java
@@ -16,6 +16,10 @@
package android.app;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
import android.util.AndroidRuntimeException;
/**
@@ -33,6 +37,10 @@
super(msg);
}
+ public RemoteServiceException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
/**
* Exception used to crash an app process when it didn't call {@link Service#startForeground}
* in time after the service was started with
@@ -44,8 +52,21 @@
/** The type ID passed to {@link IApplicationThread#scheduleCrash}. */
public static final int TYPE_ID = 1;
- public ForegroundServiceDidNotStartInTimeException(String msg) {
- super(msg);
+ private static final String KEY_SERVICE_CLASS_NAME = "serviceclassname";
+
+ public ForegroundServiceDidNotStartInTimeException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ public static Bundle createExtrasForService(@NonNull ComponentName service) {
+ Bundle b = new Bundle();
+ b.putString(KEY_SERVICE_CLASS_NAME, service.getClassName());
+ return b;
+ }
+
+ @Nullable
+ public static String getServiceClassNameFromExtras(@Nullable Bundle extras) {
+ return (extras == null) ? null : extras.getString(KEY_SERVICE_CLASS_NAME);
}
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 0145747..be6a31e 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -33,9 +33,12 @@
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.contentcapture.ContentCaptureManager;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -729,6 +732,7 @@
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, id,
notification, 0, FOREGROUND_SERVICE_TYPE_MANIFEST);
+ clearStartForegroundServiceStackTrace();
} catch (RemoteException ex) {
}
}
@@ -782,6 +786,7 @@
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, id,
notification, 0, foregroundServiceType);
+ clearStartForegroundServiceStackTrace();
} catch (RemoteException ex) {
}
}
@@ -956,4 +961,34 @@
private IActivityManager mActivityManager = null;
@UnsupportedAppUsage
private boolean mStartCompatibility = false;
+
+ /**
+ * This keeps track of the stacktrace where Context.startForegroundService() was called
+ * for each service class. We use that when we crash the app for not calling
+ * {@link #startForeground} in time, in {@link ActivityThread#throwRemoteServiceException}.
+ */
+ @GuardedBy("sStartForegroundServiceStackTraces")
+ private static final ArrayMap<String, StackTrace> sStartForegroundServiceStackTraces =
+ new ArrayMap<>();
+
+ /** @hide */
+ public static void setStartForegroundServiceStackTrace(
+ @NonNull String className, @NonNull StackTrace stacktrace) {
+ synchronized (sStartForegroundServiceStackTraces) {
+ sStartForegroundServiceStackTraces.put(className, stacktrace);
+ }
+ }
+
+ private void clearStartForegroundServiceStackTrace() {
+ synchronized (sStartForegroundServiceStackTraces) {
+ sStartForegroundServiceStackTraces.remove(this.getClassName());
+ }
+ }
+
+ /** @hide */
+ public static StackTrace getStartForegroundServiceStackTrace(@NonNull String className) {
+ synchronized (sStartForegroundServiceStackTraces) {
+ return sStartForegroundServiceStackTraces.get(className);
+ }
+ }
}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/app/StackTrace.java
similarity index 75%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to core/java/android/app/StackTrace.java
index 3729c09..ec058f8 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/java/android/app/StackTrace.java
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-package android.window;
+package android.app;
/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
+ * An Exception subclass that's used only for logging stacktraces.
* @hide
*/
-parcelable TaskFragmentAppearedInfo;
+public class StackTrace extends Exception {
+ public StackTrace(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 74e2858..089c269 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -196,9 +196,12 @@
import android.permission.PermissionManager;
import android.print.IPrintManager;
import android.print.PrintManager;
+import android.safetycenter.SafetyCenterFrameworkInitializer;
import android.scheduling.SchedulingFrameworkInitializer;
import android.security.FileIntegrityManager;
import android.security.IFileIntegrityService;
+import android.security.attestationverification.AttestationVerificationManager;
+import android.security.attestationverification.IAttestationVerificationManagerService;
import android.service.oemlock.IOemLockService;
import android.service.oemlock.OemLockManager;
import android.service.persistentdata.IPersistentDataBlockService;
@@ -1424,6 +1427,19 @@
return new FileIntegrityManager(ctx.getOuterContext(),
IFileIntegrityService.Stub.asInterface(b));
}});
+
+ registerService(Context.ATTESTATION_VERIFICATION_SERVICE,
+ AttestationVerificationManager.class,
+ new CachedServiceFetcher<AttestationVerificationManager>() {
+ @Override
+ public AttestationVerificationManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.ATTESTATION_VERIFICATION_SERVICE);
+ return new AttestationVerificationManager(ctx.getOuterContext(),
+ IAttestationVerificationManagerService.Stub.asInterface(b));
+ }});
+
//CHECKSTYLE:ON IndentationCheck
registerService(Context.APP_INTEGRITY_SERVICE, AppIntegrityManager.class,
new CachedServiceFetcher<AppIntegrityManager>() {
@@ -1530,6 +1546,7 @@
SchedulingFrameworkInitializer.registerServiceWrappers();
SupplementalProcessFrameworkInitializer.registerServiceWrappers();
UwbFrameworkInitializer.registerServiceWrappers();
+ SafetyCenterFrameworkInitializer.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index ddde272..95b00c1 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -219,6 +219,24 @@
public boolean isResizeable;
/**
+ * Minimal width of the task when it's resizeable.
+ * @hide
+ */
+ public int minWidth;
+
+ /**
+ * Minimal height of the task when it's resizeable.
+ * @hide
+ */
+ public int minHeight;
+
+ /**
+ * The default minimal size of the task used when a minWidth or minHeight is not specified.
+ * @hide
+ */
+ public int defaultMinSize;
+
+ /**
* Relative position of the task's top left corner in the parent container.
* @hide
*/
@@ -419,6 +437,9 @@
displayCutoutInsets = source.readTypedObject(Rect.CREATOR);
topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
isResizeable = source.readBoolean();
+ minWidth = source.readInt();
+ minHeight = source.readInt();
+ defaultMinSize = source.readInt();
source.readBinderList(launchCookies);
positionInParent = source.readTypedObject(Point.CREATOR);
parentTaskId = source.readInt();
@@ -459,6 +480,9 @@
dest.writeTypedObject(displayCutoutInsets, flags);
dest.writeTypedObject(topActivityInfo, flags);
dest.writeBoolean(isResizeable);
+ dest.writeInt(minWidth);
+ dest.writeInt(minHeight);
+ dest.writeInt(defaultMinSize);
dest.writeBinderList(launchCookies);
dest.writeTypedObject(positionInParent, flags);
dest.writeInt(parentTaskId);
@@ -484,6 +508,9 @@
+ " supportsMultiWindow=" + supportsMultiWindow
+ " resizeMode=" + resizeMode
+ " isResizeable=" + isResizeable
+ + " minWidth=" + minWidth
+ + " minHeight=" + minHeight
+ + " defaultMinSize=" + defaultMinSize
+ " token=" + token
+ " topActivityType=" + topActivityType
+ " pictureInPictureParams=" + pictureInPictureParams
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 828b171..58ded71 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -638,7 +638,7 @@
final IAccessibilityServiceConnection connection;
synchronized (mLock) {
throwIfNotConnectedLocked();
- AccessibilityInteractionClient.getInstance().clearCache();
+ AccessibilityInteractionClient.getInstance().clearCache(mConnectionId);
connection = AccessibilityInteractionClient.getInstance()
.getConnection(mConnectionId);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 311a60d..cf95ffe 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3471,7 +3471,10 @@
* Setting custom, overly-complicated password requirements leads to passwords that are hard
* for users to remember and may not provide any security benefits given as Android uses
* hardware-backed throttling to thwart online and offline brute-forcing of the device's
- * screen lock.
+ * screen lock. Company-owned devices (fully-managed and organization-owned managed profile
+ * devices) are able to continue using this method, though it is recommended that
+ * {@link #setRequiredPasswordComplexity(int)} should be used instead.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param quality The new desired quality. One of {@link #PASSWORD_QUALITY_UNSPECIFIED},
* {@link #PASSWORD_QUALITY_BIOMETRIC_WEAK},
@@ -7269,6 +7272,9 @@
* Returns the current runtime nearby notification streaming policy set by the device or profile
* owner.
*/
+ @RequiresPermission(
+ value = android.Manifest.permission.READ_NEARBY_STREAMING_POLICY,
+ conditional = true)
public @NearbyStreamingPolicy int getNearbyNotificationStreamingPolicy() {
return getNearbyNotificationStreamingPolicy(myUserId());
}
@@ -7309,6 +7315,9 @@
/**
* Returns the current runtime nearby app streaming policy set by the device or profile owner.
*/
+ @RequiresPermission(
+ value = android.Manifest.permission.READ_NEARBY_STREAMING_POLICY,
+ conditional = true)
public @NearbyStreamingPolicy int getNearbyAppStreamingPolicy() {
return getNearbyAppStreamingPolicy(myUserId());
}
@@ -7867,7 +7876,7 @@
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
})
public ComponentName getDeviceOwnerComponentOnAnyUser() {
return getDeviceOwnerComponentInner(/* callingUserOnly =*/ false);
@@ -7980,8 +7989,8 @@
* Called by the system to find out whether the device is managed by a Device Owner.
*
* @return whether the device is managed by a Device Owner.
- * @throws SecurityException if the caller is not the device owner, does not hold the
- * MANAGE_USERS permission and is not the system.
+ * @throws SecurityException if the caller is not the device owner, does not hold
+ * MANAGE_USERS or MANAGE_PROFILE_AND_DEVICE_OWNERS permissions and is not the system.
*
* @hide
*/
@@ -8002,7 +8011,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
public String getDeviceOwnerNameOnAnyUser() {
throwIfParentInstance("getDeviceOwnerNameOnAnyUser");
if (mService != null) {
@@ -8392,7 +8404,10 @@
* @throws IllegalArgumentException if the userId is invalid.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
public @Nullable String getProfileOwnerNameAsUser(int userId) throws IllegalArgumentException {
throwIfParentInstance("getProfileOwnerNameAsUser");
if (mService != null) {
@@ -9131,7 +9146,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_ADMIN_POLICY})
public @Nullable List<String> getPermittedAccessibilityServices(int userId) {
throwIfParentInstance("getPermittedAccessibilityServices");
if (mService != null) {
@@ -9268,12 +9285,14 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_ADMIN_POLICY})
public @Nullable List<String> getPermittedInputMethodsForCurrentUser() {
throwIfParentInstance("getPermittedInputMethodsForCurrentUser");
if (mService != null) {
try {
- return mService.getPermittedInputMethodsForCurrentUser();
+ return mService.getPermittedInputMethodsAsUser(UserHandle.myUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -9282,6 +9301,34 @@
}
/**
+ * Returns the list of input methods permitted.
+ *
+ * <p>When this method returns empty list means all input methods are allowed, if a non-empty
+ * list is returned it will contain the intersection of the permitted lists for any device or
+ * profile owners that apply to this user. It will also include any system input methods.
+ *
+ * @return List of input method package names.
+ * @hide
+ */
+ @UserHandleAware
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ android.Manifest.permission.MANAGE_USERS
+ }, conditional = true)
+ public @NonNull List<String> getPermittedInputMethods() {
+ throwIfParentInstance("getPermittedInputMethods");
+ List<String> result = null;
+ if (mService != null) {
+ try {
+ result = mService.getPermittedInputMethodsAsUser(myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return result != null ? result : Collections.emptyList();
+ }
+
+ /**
* Called by a profile owner of a managed profile to set the packages that are allowed to use
* a {@link android.service.notification.NotificationListenerService} in the primary user to
* see notifications from the managed profile. By default all packages are permitted by this
@@ -11930,7 +11977,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
@UserProvisioningState
public int getUserProvisioningState() {
throwIfParentInstance("getUserProvisioningState");
@@ -13439,7 +13489,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
public boolean isManagedKiosk() {
throwIfParentInstance("isManagedKiosk");
if (mService != null) {
@@ -13468,7 +13521,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
public boolean isUnattendedManagedKiosk() {
throwIfParentInstance("isUnattendedManagedKiosk");
if (mService != null) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index cf48594..b9fcdf5 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -243,7 +243,7 @@
boolean setPermittedInputMethods(in ComponentName admin,in List<String> packageList, boolean parent);
List<String> getPermittedInputMethods(in ComponentName admin, boolean parent);
- List<String> getPermittedInputMethodsForCurrentUser();
+ List<String> getPermittedInputMethodsAsUser(int userId);
boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId, boolean parent);
boolean setPermittedCrossProfileNotificationListeners(in ComponentName admin, in List<String> packageList);
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index a71cffe..ceab02f 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -96,6 +96,13 @@
String SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE = "suggest_telephony_time_zone";
/**
+ * A shell command that enables telephony time zone fallback. See {@link
+ * com.android.server.timezonedetector.TimeZoneDetectorStrategy} for details.
+ * @hide
+ */
+ String SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK = "enable_telephony_fallback";
+
+ /**
* A shared utility method to create a {@link ManualTimeZoneSuggestion}.
*
* @hide
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index cf00cbd..20122fb 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -51,7 +51,6 @@
import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -59,7 +58,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.SynchronousResultReceiver;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Pair;
@@ -69,6 +67,7 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -82,7 +81,6 @@
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
-import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
@@ -402,6 +400,16 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ScanMode {}
+ /** @hide */
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScanModeStatusCode {}
+
/**
* Indicates that both inquiry scan and page scan are disabled on the local
* Bluetooth adapter. Therefore this device is neither discoverable
@@ -1618,7 +1626,7 @@
return mService.getScanMode(mAttributionSource);
}
} catch (RemoteException e) {
- Log.e(TAG, "", e);
+ throw e.rethrowFromSystemServer();
} finally {
mServiceLock.readLock().unlock();
}
@@ -1626,143 +1634,110 @@
}
/**
- * Set the Bluetooth scan mode of the local Bluetooth adapter.
- * <p>The Bluetooth scan mode determines if the local adapter is
- * connectable and/or discoverable from remote Bluetooth devices.
- * <p>For privacy reasons, discoverable mode is automatically turned off
- * after <code>durationMillis</code> milliseconds. For example, 120000 milliseconds should be
- * enough for a remote device to initiate and complete its discovery process.
- * <p>Valid scan mode values are:
- * {@link #SCAN_MODE_NONE},
- * {@link #SCAN_MODE_CONNECTABLE},
- * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
- * <p>If Bluetooth state is not {@link #STATE_ON}, this API
- * will return false. After turning on Bluetooth,
- * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
- * to get the updated value.
+ * Set the local Bluetooth adapter connectablility and discoverability.
+ * <p>If the scan mode is set to {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
+ * it will change to {@link #SCAN_MODE_CONNECTABLE} after the discoverable timeout.
+ * The discoverable timeout can be set with {@link #setDiscoverableTimeout} and
+ * checked with {@link #getDiscoverableTimeout}. By default, the timeout is usually
+ * 120 seconds on phones which is enough for a remote device to initiate and complete
+ * its discovery process.
* <p>Applications cannot set the scan mode. They should use
- * <code>startActivityForResult(
- * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
- * </code>instead.
+ * {@link #ACTION_REQUEST_DISCOVERABLE} instead.
*
- * @param mode valid scan mode
- * @param durationMillis time in milliseconds to apply scan mode, only used for {@link
- * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}
- * @return true if the scan mode was set, false otherwise
+ * @param mode represents the desired state of the local device scan mode
+ *
+ * @return status code indicating whether the scan mode was successfully set
* @hide
*/
- @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which "
- + "shows UI that confirms the user wants to go into discoverable mode.")
- @RequiresLegacyBluetoothPermission
+ @SystemApi
@RequiresBluetoothScanPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
- public boolean setScanMode(@ScanMode int mode, long durationMillis) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_SCAN,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @ScanModeStatusCode
+ public int setScanMode(@ScanMode int mode) {
if (getState() != STATE_ON) {
- return false;
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
try {
mServiceLock.readLock().lock();
if (mService != null) {
- int durationSeconds = Math.toIntExact(durationMillis / 1000);
- return mService.setScanMode(mode, durationSeconds, mAttributionSource);
+ return mService.setScanMode(mode, mAttributionSource);
}
} catch (RemoteException e) {
- Log.e(TAG, "", e);
- } catch (ArithmeticException ex) {
- Log.e(TAG, "setScanMode: Duration in seconds outside of the bounds of an int");
- throw new IllegalArgumentException("Duration not in bounds. In seconds, the "
- + "durationMillis must be in the range of an int");
+ throw e.rethrowFromSystemServer();
} finally {
mServiceLock.readLock().unlock();
}
- return false;
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
}
/**
- * Set the Bluetooth scan mode of the local Bluetooth adapter.
- * <p>The Bluetooth scan mode determines if the local adapter is
- * connectable and/or discoverable from remote Bluetooth devices.
- * <p>For privacy reasons, discoverable mode is automatically turned off
- * after <code>duration</code> seconds. For example, 120 seconds should be
- * enough for a remote device to initiate and complete its discovery
- * process.
- * <p>Valid scan mode values are:
- * {@link #SCAN_MODE_NONE},
- * {@link #SCAN_MODE_CONNECTABLE},
- * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
- * <p>If Bluetooth state is not {@link #STATE_ON}, this API
- * will return false. After turning on Bluetooth,
- * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
- * to get the updated value.
- * <p>Applications cannot set the scan mode. They should use
- * <code>startActivityForResult(
- * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
- * </code>instead.
+ * Get the timeout duration of the {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
*
- * @param mode valid scan mode
- * @return true if the scan mode was set, false otherwise
+ * @return the duration of the discoverable timeout or null if an error has occurred
+ */
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
+ public @Nullable Duration getDiscoverableTimeout() {
+ if (getState() != STATE_ON) {
+ return null;
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ long timeout = mService.getDiscoverableTimeout(mAttributionSource);
+ return (timeout == -1) ? null : Duration.ofSeconds(timeout);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return null;
+ }
+
+ /**
+ * Set the total time the Bluetooth local adapter will stay discoverable when
+ * {@link #setScanMode} is called with {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE} mode.
+ * After this timeout, the scan mode will fallback to {@link #SCAN_MODE_CONNECTABLE}.
+ * <p>If <code>timeout</code> is set to 0, no timeout will occur and the scan mode will
+ * be persisted until a subsequent call to {@link #setScanMode}.
+ *
+ * @param timeout represents the total duration the local Bluetooth adapter will remain
+ * discoverable, or no timeout if set to 0
+ * @return whether the timeout was successfully set
+ * @throws IllegalArgumentException if <code>timeout</code> duration in seconds is more
+ * than {@link Integer#MAX_VALUE}
* @hide
*/
- @UnsupportedAppUsage
- @RequiresLegacyBluetoothPermission
+ @SystemApi
@RequiresBluetoothScanPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
- public boolean setScanMode(@ScanMode int mode) {
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_SCAN,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @ScanModeStatusCode
+ public int setDiscoverableTimeout(@NonNull Duration timeout) {
if (getState() != STATE_ON) {
- return false;
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ if (timeout.toSeconds() > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("Timeout in seconds must be less or equal to "
+ + Integer.MAX_VALUE);
}
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource);
+ return mService.setDiscoverableTimeout(timeout.toSeconds(), mAttributionSource);
}
} catch (RemoteException e) {
- Log.e(TAG, "", e);
+ throw e.rethrowFromSystemServer();
} finally {
mServiceLock.readLock().unlock();
}
- return false;
- }
-
- /** @hide */
- @UnsupportedAppUsage
- @RequiresBluetoothScanPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
- public int getDiscoverableTimeout() {
- if (getState() != STATE_ON) {
- return -1;
- }
- try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- return mService.getDiscoverableTimeout(mAttributionSource);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- } finally {
- mServiceLock.readLock().unlock();
- }
- return -1;
- }
-
- /** @hide */
- @UnsupportedAppUsage
- @RequiresBluetoothScanPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
- public void setDiscoverableTimeout(int timeout) {
- if (getState() != STATE_ON) {
- return;
- }
- try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- mService.setDiscoverableTimeout(timeout, mAttributionSource);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- } finally {
- mServiceLock.readLock().unlock();
- }
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
}
/**
@@ -2424,38 +2399,6 @@
}
/**
- * Return the record of {@link BluetoothActivityEnergyInfo} object that
- * has the activity and energy info. This can be used to ascertain what
- * the controller has been up to, since the last sample.
- *
- * @param updateType Type of info, cached vs refreshed.
- * @return a record with {@link BluetoothActivityEnergyInfo} or null if report is unavailable or
- * unsupported
- * @hide
- * @deprecated use the asynchronous {@link #requestControllerActivityEnergyInfo(ResultReceiver)}
- * instead.
- */
- @Deprecated
- @RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED,
- })
- public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
- SynchronousResultReceiver receiver = new SynchronousResultReceiver();
- requestControllerActivityEnergyInfo(receiver);
- try {
- SynchronousResultReceiver.Result result = receiver.awaitResult(1000);
- if (result.bundle != null) {
- return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
- }
- } catch (TimeoutException e) {
- Log.e(TAG, "getControllerActivityEnergyInfo timed out");
- }
- return null;
- }
-
- /**
* Request the record of {@link BluetoothActivityEnergyInfo} object that
* has the activity and energy info. This can be used to ascertain what
* the controller has been up to, since the last sample.
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 7fe18a0..69525b5 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -327,21 +328,26 @@
return Arrays.copyOfRange(bytes, 1, bytes.length);
}
- /** @hide */
- @UnsupportedAppUsage
public static final int PROFILE_HEADSET = 0;
- /** @hide */
- @UnsupportedAppUsage
+
public static final int PROFILE_A2DP = 1;
+
/** @hide */
+ @SystemApi
public static final int PROFILE_OPP = 2;
- /** @hide */
+
public static final int PROFILE_HID = 3;
+
/** @hide */
+ @SystemApi
public static final int PROFILE_PANU = 4;
+
/** @hide */
+ @SystemApi
public static final int PROFILE_NAP = 5;
+
/** @hide */
+ @SystemApi
public static final int PROFILE_A2DP_SINK = 6;
/**
@@ -350,11 +356,9 @@
* given class bits might support specified profile. It is not accurate for all
* devices. It tries to err on the side of false positives.
*
- * @param profile The profile to be checked
- * @return True if this device might support specified profile.
- * @hide
+ * @param profile the profile to be checked
+ * @return whether this device supports specified profile
*/
- @UnsupportedAppUsage
public boolean doesClassMatch(int profile) {
if (profile == PROFILE_A2DP) {
if (hasService(Service.RENDER)) {
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 3e799de..08e0178 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
@@ -26,6 +28,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -709,33 +713,85 @@
* notification
* @return true, if the notification has been triggered successfully
* @throws IllegalArgumentException
+ *
+ * @deprecated Use {@link BluetoothGattServer#notifyCharacteristicChanged(BluetoothDevice,
+ * BluetoothGattCharacteristic, boolean, byte[])} as this is not memory safe.
*/
+ @Deprecated
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean notifyCharacteristicChanged(BluetoothDevice device,
BluetoothGattCharacteristic characteristic, boolean confirm) {
+ return notifyCharacteristicChanged(device, characteristic, confirm,
+ characteristic.getValue()) == BluetoothStatusCodes.SUCCESS;
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
+ BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
+ BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
+ BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
+ BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
+ BluetoothStatusCodes.ERROR_UNKNOWN
+ })
+ public @interface NotifyCharacteristicReturnValues{}
+
+ /**
+ * Send a notification or indication that a local characteristic has been
+ * updated.
+ *
+ * <p>A notification or indication is sent to the remote device to signal
+ * that the characteristic has been updated. This function should be invoked
+ * for every client that requests notifications/indications by writing
+ * to the "Client Configuration" descriptor for the given characteristic.
+ *
+ * @param device the remote device to receive the notification/indication
+ * @param characteristic the local characteristic that has been updated
+ * @param confirm {@code true} to request confirmation from the client (indication) or
+ * {@code false} to send a notification
+ * @param value the characteristic value
+ * @return whether the notification has been triggered successfully
+ * @throws IllegalArgumentException if the characteristic value or service is null
+ */
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @NotifyCharacteristicReturnValues
+ public int notifyCharacteristicChanged(@NonNull BluetoothDevice device,
+ @NonNull BluetoothGattCharacteristic characteristic, boolean confirm,
+ @NonNull byte[] value) {
if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
- if (mService == null || mServerIf == 0) return false;
+ if (mService == null || mServerIf == 0) {
+ return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
+ }
+ if (characteristic == null) {
+ throw new IllegalArgumentException("characteristic must not be null");
+ }
+ if (device == null) {
+ throw new IllegalArgumentException("device must not be null");
+ }
BluetoothGattService service = characteristic.getService();
- if (service == null) return false;
-
- if (characteristic.getValue() == null) {
- throw new IllegalArgumentException("Chracteristic value is empty. Use "
- + "BluetoothGattCharacteristic#setvalue to update");
+ if (service == null) {
+ throw new IllegalArgumentException("Characteristic must have a non-null service");
+ }
+ if (value == null) {
+ throw new IllegalArgumentException("Characteristic value must not be null");
}
try {
- mService.sendNotification(mServerIf, device.getAddress(),
+ return mService.sendNotification(mServerIf, device.getAddress(),
characteristic.getInstanceId(), confirm,
- characteristic.getValue(), mAttributionSource);
+ value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- return false;
+ throw e.rethrowFromSystemServer();
}
-
- return true;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 1655b62..db5b751 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -18,7 +18,6 @@
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.LocalSocket;
@@ -266,7 +265,7 @@
throw new IOException("bt socket acept failed");
}
- as.mPfd = new ParcelFileDescriptor(fds[0]);
+ as.mPfd = ParcelFileDescriptor.dup(fds[0]);
as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]);
as.mSocketIS = as.mSocket.getInputStream();
as.mSocketOS = as.mSocket.getOutputStream();
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
index ca01784..5ba7bb1 100644
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/core/java/android/bluetooth/BluetoothStatusCodes.java
@@ -40,7 +40,7 @@
/**
* Error code indicating that the API call was initiated by neither the system nor the active
- * Zuser
+ * user
*/
public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2;
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index ab1eb1f..3f02aa2 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -15,29 +15,24 @@
*/
package android.companion;
-import static android.companion.DeviceId.TYPE_MAC_ADDRESS;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.UserIdInt;
+import android.net.MacAddress;
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
import java.util.Objects;
-import java.util.Set;
/**
- * A record indicating that a device with a given address was confirmed by the user to be
- * associated to a given companion app
- *
- * @hide
- * TODO(b/1979395): un-hide and rename to AssociationInfo when implementing public APIs that use
- * this class.
+ * Details for a specific "association" that has been established between an app and companion
+ * device.
+ * <p>
+ * An association gives an app the ability to interact with a companion device without needing to
+ * acquire broader runtime permissions. An association only exists after the user has confirmed that
+ * an app should have access to a companion device.
*/
public final class AssociationInfo implements Parcelable {
/**
@@ -45,15 +40,16 @@
* Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
* {@code disassociate()} API call).
*/
- private final int mAssociationId;
+ private final int mId;
private final @UserIdInt int mUserId;
private final @NonNull String mPackageName;
- private final @NonNull List<DeviceId> mDeviceIds;
+ private final @Nullable MacAddress mDeviceMacAddress;
+ private final @Nullable CharSequence mDisplayName;
private final @Nullable String mDeviceProfile;
- private final boolean mManagedByCompanionApp;
+ private final boolean mSelfManaged;
private boolean mNotifyOnDeviceNearby;
private final long mTimeApprovedMs;
@@ -63,23 +59,28 @@
*
* @hide
*/
- public AssociationInfo(int associationId, @UserIdInt int userId, @NonNull String packageName,
- @NonNull List<DeviceId> deviceIds, @Nullable String deviceProfile,
- boolean managedByCompanionApp, boolean notifyOnDeviceNearby, long timeApprovedMs) {
- if (associationId <= 0) {
+ public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
+ @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
+ @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
+ long timeApprovedMs) {
+ if (id <= 0) {
throw new IllegalArgumentException("Association ID should be greater than 0");
}
- validateDeviceIds(deviceIds);
+ if (macAddress == null && displayName == null) {
+ throw new IllegalArgumentException("MAC address and the Display Name must NOT be null "
+ + "at the same time");
+ }
- mAssociationId = associationId;
+ mId = id;
mUserId = userId;
mPackageName = packageName;
+ mDeviceMacAddress = macAddress;
+ mDisplayName = displayName;
mDeviceProfile = deviceProfile;
- mDeviceIds = new ArrayList<>(deviceIds);
- mManagedByCompanionApp = managedByCompanionApp;
+ mSelfManaged = selfManaged;
mNotifyOnDeviceNearby = notifyOnDeviceNearby;
mTimeApprovedMs = timeApprovedMs;
}
@@ -87,55 +88,66 @@
/**
* @return the unique ID of this association record.
*/
- public int getAssociationId() {
- return mAssociationId;
+ public int getId() {
+ return mId;
}
- /** @hide */
- public int getUserId() {
+ /**
+ * @return the ID of the user who "owns" this association.
+ * @hide
+ */
+ public @UserIdInt int getUserId() {
return mUserId;
}
- /** @hide */
+ /**
+ * @return the package name of the app which this association refers to.
+ * @hide
+ */
+ @SystemApi
public @NonNull String getPackageName() {
return mPackageName;
}
/**
- * @return list of the device's IDs. At any time a device has at least 1 ID.
+ * @return the MAC address of the device.
*/
- public @NonNull List<DeviceId> getDeviceIds() {
- return Collections.unmodifiableList(mDeviceIds);
- }
-
- /**
- * @param type type of the ID.
- * @return ID of the type if the device has such ID, {@code null} otherwise.
- */
- public @Nullable String getIdOfType(@NonNull String type) {
- for (int i = mDeviceIds.size() - 1; i >= 0; i--) {
- final DeviceId id = mDeviceIds.get(i);
- if (Objects.equals(mDeviceIds.get(i).getType(), type)) return id.getValue();
- }
- return null;
+ public @Nullable MacAddress getDeviceMacAddress() {
+ return mDeviceMacAddress;
}
/** @hide */
- public @NonNull String getDeviceMacAddress() {
- return Objects.requireNonNull(getIdOfType(TYPE_MAC_ADDRESS),
- "MAC address of this device is not specified.");
+ public @Nullable String getDeviceMacAddressAsString() {
+ return mDeviceMacAddress != null ? mDeviceMacAddress.toString().toUpperCase() : null;
}
/**
- * @return the profile of the device.
+ * @return the display name of the companion device (optionally) provided by the companion
+ * application.
+ *
+ * @see AssociationRequest.Builder#setDisplayName(CharSequence)
+ */
+ public @Nullable CharSequence getDisplayName() {
+ return mDisplayName;
+ }
+
+ /**
+ * @return the companion device profile used when establishing this
+ * association, or {@code null} if no specific profile was used.
+ * @see AssociationRequest.Builder#setDeviceProfile(String)
*/
public @Nullable String getDeviceProfile() {
return mDeviceProfile;
}
- /** @hide */
- public boolean isManagedByCompanionApp() {
- return mManagedByCompanionApp;
+ /**
+ * @return whether the association is managed by the companion application it belongs to.
+ * @see AssociationRequest.Builder#setSelfManaged(boolean)
+ * @hide
+ */
+ @SystemApi
+ public boolean isSelfManaged() {
+ return mSelfManaged;
}
/**
@@ -161,15 +173,40 @@
return mUserId == userId && Objects.equals(mPackageName, packageName);
}
+ /**
+ * Utility method for checking if the association represents a device with the given MAC
+ * address.
+ *
+ * @return {@code false} if the association is "self-managed".
+ * {@code false} if the {@code addr} is {@code null} or is not a valid MAC address.
+ * Otherwise - the result of {@link MacAddress#equals(Object)}
+ *
+ * @hide
+ */
+ public boolean isLinkedTo(@Nullable String addr) {
+ if (mSelfManaged) return false;
+
+ if (addr == null) return false;
+
+ final MacAddress macAddress;
+ try {
+ macAddress = MacAddress.fromString(addr);
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ return macAddress.equals(mDeviceMacAddress);
+ }
+
@Override
public String toString() {
return "Association{"
- + "mAssociationId=" + mAssociationId
+ + "mId=" + mId
+ ", mUserId=" + mUserId
+ ", mPackageName='" + mPackageName + '\''
- + ", mDeviceIds=" + mDeviceIds
+ + ", mDeviceMacAddress=" + mDeviceMacAddress
+ + ", mDisplayName='" + mDisplayName + '\''
+ ", mDeviceProfile='" + mDeviceProfile + '\''
- + ", mManagedByCompanionApp=" + mManagedByCompanionApp
+ + ", mSelfManaged=" + mSelfManaged
+ ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
+ ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
+ '}';
@@ -180,20 +217,21 @@
if (this == o) return true;
if (!(o instanceof AssociationInfo)) return false;
final AssociationInfo that = (AssociationInfo) o;
- return mAssociationId == that.mAssociationId
+ return mId == that.mId
&& mUserId == that.mUserId
- && mManagedByCompanionApp == that.mManagedByCompanionApp
+ && mSelfManaged == that.mSelfManaged
&& mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
&& mTimeApprovedMs == that.mTimeApprovedMs
&& Objects.equals(mPackageName, that.mPackageName)
- && Objects.equals(mDeviceProfile, that.mDeviceProfile)
- && Objects.equals(mDeviceIds, that.mDeviceIds);
+ && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
+ && Objects.equals(mDisplayName, that.mDisplayName)
+ && Objects.equals(mDeviceProfile, that.mDeviceProfile);
}
@Override
public int hashCode() {
- return Objects.hash(mAssociationId, mUserId, mPackageName, mDeviceIds, mDeviceProfile,
- mManagedByCompanionApp, mNotifyOnDeviceNearby, mTimeApprovedMs);
+ return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
+ mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs);
}
@Override
@@ -203,33 +241,36 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mAssociationId);
+ dest.writeInt(mId);
dest.writeInt(mUserId);
dest.writeString(mPackageName);
- dest.writeParcelableList(mDeviceIds, 0);
+ dest.writeTypedObject(mDeviceMacAddress, 0);
+ dest.writeCharSequence(mDisplayName);
dest.writeString(mDeviceProfile);
- dest.writeBoolean(mManagedByCompanionApp);
+ dest.writeBoolean(mSelfManaged);
dest.writeBoolean(mNotifyOnDeviceNearby);
dest.writeLong(mTimeApprovedMs);
}
private AssociationInfo(@NonNull Parcel in) {
- mAssociationId = in.readInt();
+ mId = in.readInt();
mUserId = in.readInt();
mPackageName = in.readString();
- mDeviceIds = in.readParcelableList(new ArrayList<>(), DeviceId.class.getClassLoader());
+ mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR);
+ mDisplayName = in.readCharSequence();
mDeviceProfile = in.readString();
- mManagedByCompanionApp = in.readBoolean();
+ mSelfManaged = in.readBoolean();
mNotifyOnDeviceNearby = in.readBoolean();
mTimeApprovedMs = in.readLong();
}
+ @NonNull
public static final Parcelable.Creator<AssociationInfo> CREATOR =
new Parcelable.Creator<AssociationInfo>() {
@Override
@@ -242,19 +283,4 @@
return new AssociationInfo(in);
}
};
-
- private static void validateDeviceIds(@NonNull List<DeviceId> ids) {
- if (ids.isEmpty()) throw new IllegalArgumentException("Device must have at least 1 id.");
-
- // Make sure none of the IDs are null, and they all have different types.
- final Set<String> types = new HashSet<>(ids.size());
- for (int i = ids.size() - 1; i >= 0; i--) {
- final DeviceId deviceId = ids.get(i);
- if (deviceId == null) throw new IllegalArgumentException("DeviceId must not be null");
- if (!types.add(deviceId.getType())) {
- throw new IllegalArgumentException(
- "DeviceId cannot have multiple IDs of the same type");
- }
- }
- }
}
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 7d1aabc..1dc161c 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -16,6 +16,8 @@
package android.companion;
+import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED;
+
import static com.android.internal.util.CollectionUtils.emptyIfNull;
import android.Manifest;
@@ -58,9 +60,6 @@
genBuilder = false,
genConstDefs = false)
public final class AssociationRequest implements Parcelable {
-
- private static final String LOG_TAG = AssociationRequest.class.getSimpleName();
-
/**
* Device profile: watch.
*
@@ -116,7 +115,7 @@
/**
* Whether only a single device should match the provided filter.
*
- * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac
+ * When scanning for a single device with a specific {@link BluetoothDeviceFilter} mac
* address, bonded devices are also searched among. This allows to obtain the necessary app
* privileges even if the device is already paired.
*/
@@ -134,6 +133,24 @@
private @Nullable @DeviceProfile String mDeviceProfile = null;
/**
+ * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
+ * "self-managed" association.
+ */
+ private final @Nullable CharSequence mDisplayName;
+
+ /**
+ * Whether the association is to be managed by the companion application.
+ */
+ private final boolean mSelfManaged;
+
+ /**
+ * Indicates that the application would prefer the CompanionDeviceManager to collect an explicit
+ * confirmation from the user before creating an association, even if such confirmation is not
+ * required.
+ */
+ private final boolean mForceConfirmation;
+
+ /**
* The app package making the request.
*
* Populated by the system.
@@ -167,8 +184,30 @@
*/
private boolean mSkipPrompt = false;
- private void onConstructed() {
- mCreationTime = System.currentTimeMillis();
+ /**
+ * Whether the association is to be managed by the companion application.
+ *
+ * @see Builder#setSelfManaged(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(REQUEST_COMPANION_SELF_MANAGED)
+ public boolean isSelfManaged() {
+ return mSelfManaged;
+ }
+
+ /**
+ * Indicates that the application would prefer the CompanionDeviceManager to collect an explicit
+ * confirmation from the user before creating an association, even if such confirmation is not
+ * required.
+ *
+ * @see Builder#setForceConfirmation(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(REQUEST_COMPANION_SELF_MANAGED)
+ public boolean isForceConfirmation() {
+ return mForceConfirmation;
}
/** @hide */
@@ -199,20 +238,27 @@
return mDeviceFilters;
}
+ private void onConstructed() {
+ mCreationTime = System.currentTimeMillis();
+ }
+
/**
* A builder for {@link AssociationRequest}
*/
public static final class Builder extends OneTimeUseBuilder<AssociationRequest> {
private boolean mSingleDevice = false;
- @Nullable private ArrayList<DeviceFilter<?>> mDeviceFilters = null;
- private @Nullable String mDeviceProfile = null;
+ private @Nullable ArrayList<DeviceFilter<?>> mDeviceFilters = null;
+ private @Nullable String mDeviceProfile;
+ private @Nullable CharSequence mDisplayName;
+ private boolean mSelfManaged = false;
+ private boolean mForceConfirmation = false;
public Builder() {}
/**
* Whether only a single device should match the provided filter.
*
- * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac
+ * When scanning for a single device with a specific {@link BluetoothDeviceFilter} mac
* address, bonded devices are also searched among. This allows to obtain the necessary app
* privileges even if the device is already paired.
*
@@ -249,14 +295,65 @@
return this;
}
+ /**
+ * Adds a display name.
+ * Generally {@link AssociationRequest}s are not required to provide a display name, except
+ * for request for creating "self-managed" associations, which MUST provide a display name.
+ *
+ * @param displayName the display name of the device.
+ */
+ @NonNull
+ public Builder setDisplayName(@NonNull CharSequence displayName) {
+ checkNotUsed();
+ mDisplayName = Objects.requireNonNull(displayName);
+ return this;
+ }
+
+ /**
+ * Indicate whether the association would be managed by the companion application.
+ *
+ * Requests for creating "self-managed" association MUST provide a Display name.
+ *
+ * @see #setDisplayName(CharSequence)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(REQUEST_COMPANION_SELF_MANAGED)
+ @NonNull
+ public Builder setSelfManaged(boolean selfManaged) {
+ checkNotUsed();
+ mSelfManaged = selfManaged;
+ return this;
+ }
+
+ /**
+ * Indicates whether the application would prefer the CompanionDeviceManager to collect an
+ * explicit confirmation from the user before creating an association, even if such
+ * confirmation is not required.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(REQUEST_COMPANION_SELF_MANAGED)
+ @NonNull
+ public Builder setForceConfirmation(boolean forceConfirmation) {
+ checkNotUsed();
+ mForceConfirmation = forceConfirmation;
+ return this;
+ }
+
/** @inheritDoc */
@NonNull
@Override
public AssociationRequest build() {
markUsed();
- return new AssociationRequest(
- mSingleDevice, emptyIfNull(mDeviceFilters),
- mDeviceProfile, null, null, -1L, false);
+ if (mSelfManaged && mDisplayName == null) {
+ throw new IllegalStateException("Request for a self-managed association MUST "
+ + "provide the display name of the device");
+ }
+ return new AssociationRequest(mSingleDevice, emptyIfNull(mDeviceFilters),
+ mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation,
+ null, null, -1L, false);
}
}
@@ -283,13 +380,22 @@
* @param singleDevice
* Whether only a single device should match the provided filter.
*
- * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac
+ * When scanning for a single device with a specific {@link BluetoothDeviceFilter} mac
* address, bonded devices are also searched among. This allows to obtain the necessary app
* privileges even if the device is already paired.
* @param deviceFilters
* If set, only devices matching either of the given filters will be shown to the user
* @param deviceProfile
* If set, association will be requested as a corresponding kind of device
+ * @param displayName
+ * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
+ * "self-managed" association.
+ * @param selfManaged
+ * Whether the association is to be managed by the companion application.
+ * @param forceConfirmation
+ * Indicates that the application would prefer the CompanionDeviceManager to collect an explicit
+ * confirmation from the user before creating an association, even if such confirmation is not
+ * required.
* @param callingPackage
* The app package making the request.
*
@@ -311,6 +417,9 @@
boolean singleDevice,
@NonNull List<DeviceFilter<?>> deviceFilters,
@Nullable @DeviceProfile String deviceProfile,
+ @Nullable CharSequence displayName,
+ boolean selfManaged,
+ boolean forceConfirmation,
@Nullable String callingPackage,
@Nullable String deviceProfilePrivilegesDescription,
long creationTime,
@@ -322,6 +431,9 @@
this.mDeviceProfile = deviceProfile;
com.android.internal.util.AnnotationValidations.validate(
DeviceProfile.class, null, mDeviceProfile);
+ this.mDisplayName = displayName;
+ this.mSelfManaged = selfManaged;
+ this.mForceConfirmation = forceConfirmation;
this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
this.mCreationTime = creationTime;
@@ -341,6 +453,17 @@
}
/**
+ * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
+ * "self-managed" association.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable CharSequence getDisplayName() {
+ return mDisplayName;
+ }
+
+ /**
* The app package making the request.
*
* Populated by the system.
@@ -396,6 +519,9 @@
"singleDevice = " + mSingleDevice + ", " +
"deviceFilters = " + mDeviceFilters + ", " +
"deviceProfile = " + mDeviceProfile + ", " +
+ "displayName = " + mDisplayName + ", " +
+ "selfManaged = " + mSelfManaged + ", " +
+ "forceConfirmation = " + mForceConfirmation + ", " +
"callingPackage = " + mCallingPackage + ", " +
"deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + ", " +
"creationTime = " + mCreationTime + ", " +
@@ -419,6 +545,9 @@
&& mSingleDevice == that.mSingleDevice
&& Objects.equals(mDeviceFilters, that.mDeviceFilters)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
+ && Objects.equals(mDisplayName, that.mDisplayName)
+ && mSelfManaged == that.mSelfManaged
+ && mForceConfirmation == that.mForceConfirmation
&& Objects.equals(mCallingPackage, that.mCallingPackage)
&& Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription)
&& mCreationTime == that.mCreationTime
@@ -435,6 +564,9 @@
_hash = 31 * _hash + Boolean.hashCode(mSingleDevice);
_hash = 31 * _hash + Objects.hashCode(mDeviceFilters);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
+ _hash = 31 * _hash + Objects.hashCode(mDisplayName);
+ _hash = 31 * _hash + Boolean.hashCode(mSelfManaged);
+ _hash = 31 * _hash + Boolean.hashCode(mForceConfirmation);
_hash = 31 * _hash + Objects.hashCode(mCallingPackage);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
_hash = 31 * _hash + Long.hashCode(mCreationTime);
@@ -448,15 +580,19 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
- byte flg = 0;
+ int flg = 0;
if (mSingleDevice) flg |= 0x1;
- if (mSkipPrompt) flg |= 0x40;
+ if (mSelfManaged) flg |= 0x10;
+ if (mForceConfirmation) flg |= 0x20;
+ if (mSkipPrompt) flg |= 0x200;
if (mDeviceProfile != null) flg |= 0x4;
- if (mCallingPackage != null) flg |= 0x8;
- if (mDeviceProfilePrivilegesDescription != null) flg |= 0x10;
- dest.writeByte(flg);
+ if (mDisplayName != null) flg |= 0x8;
+ if (mCallingPackage != null) flg |= 0x40;
+ if (mDeviceProfilePrivilegesDescription != null) flg |= 0x80;
+ dest.writeInt(flg);
dest.writeParcelableList(mDeviceFilters, flags);
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
+ if (mDisplayName != null) dest.writeCharSequence(mDisplayName);
if (mCallingPackage != null) dest.writeString(mCallingPackage);
if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription);
dest.writeLong(mCreationTime);
@@ -473,14 +609,17 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
- byte flg = in.readByte();
+ int flg = in.readInt();
boolean singleDevice = (flg & 0x1) != 0;
- boolean skipPrompt = (flg & 0x40) != 0;
+ boolean selfManaged = (flg & 0x10) != 0;
+ boolean forceConfirmation = (flg & 0x20) != 0;
+ boolean skipPrompt = (flg & 0x200) != 0;
List<DeviceFilter<?>> deviceFilters = new ArrayList<>();
in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader());
String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
- String callingPackage = (flg & 0x8) == 0 ? null : in.readString();
- String deviceProfilePrivilegesDescription = (flg & 0x10) == 0 ? null : in.readString();
+ CharSequence displayName = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence();
+ String callingPackage = (flg & 0x40) == 0 ? null : in.readString();
+ String deviceProfilePrivilegesDescription = (flg & 0x80) == 0 ? null : in.readString();
long creationTime = in.readLong();
this.mSingleDevice = singleDevice;
@@ -490,6 +629,9 @@
this.mDeviceProfile = deviceProfile;
com.android.internal.util.AnnotationValidations.validate(
DeviceProfile.class, null, mDeviceProfile);
+ this.mDisplayName = displayName;
+ this.mSelfManaged = selfManaged;
+ this.mForceConfirmation = forceConfirmation;
this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
this.mCreationTime = creationTime;
@@ -513,10 +655,10 @@
};
@DataClass.Generated(
- time = 1635190605212L,
+ time = 1637228802427L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\nprivate void onConstructed()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
+ inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate final @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isSelfManaged()\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isForceConfirmation()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nprivate void onConstructed()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 6719a69..2b12f12 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -16,23 +16,26 @@
package android.companion;
-import android.Manifest;
+import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING;
+import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION;
+import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
import android.app.Activity;
-import android.app.Application;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.net.MacAddress;
-import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -40,10 +43,16 @@
import android.util.ExceptionUtils;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CollectionUtils;
+
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
-import java.util.function.BiConsumer;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* System level service for managing companion devices
@@ -75,10 +84,21 @@
* <li>for Bluetooth LE - {@link android.bluetooth.le.ScanResult}</li>
* <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
* </ul>
+ *
+ * @deprecated use {@link #EXTRA_ASSOCIATION} instead.
*/
+ @Deprecated
public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
/**
+ * Extra field name for the {@link AssociationInfo} object, included into
+ * {@link android.content.Intent} which application receive in
+ * {@link Activity#onActivityResult(int, int, Intent)} after the application's
+ * {@link AssociationRequest} was successfully processed and an association was created.
+ */
+ public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION";
+
+ /**
* The package name of the companion device discovery component.
*
* @hide
@@ -87,30 +107,121 @@
"com.android.companiondevicemanager";
/**
- * A callback to receive once at least one suitable device is found, or the search failed
- * (e.g. timed out)
+ * Callback for applications to receive updates about and the outcome of
+ * {@link AssociationRequest} issued via {@code associate()} call.
+ *
+ * <p>
+ * The {@link Callback#onAssociationPending(IntentSender)} is invoked after the
+ * {@link AssociationRequest} has been checked by the Companion Device Manager Service and is
+ * pending user's approval.
+ *
+ * The {@link IntentSender} received as an argument to
+ * {@link Callback#onAssociationPending(IntentSender)} "encapsulates" an {@link Activity}
+ * that has UI for the user to:
+ * <ul>
+ * <li>
+ * choose the device to associate the application with (if multiple eligible devices are
+ * available)
+ * </li>
+ * <li>confirm the association</li>
+ * <li>
+ * approve the privileges the application will be granted if the association is to be created
+ * </li>
+ * </ul>
+ *
+ * If the Companion Device Manager Service needs to scan for the devices, the {@link Activity}
+ * will also display the status and the progress of the scan.
+ *
+ * Note that Companion Device Manager Service will only start the scanning after the
+ * {@link Activity} was launched and became visible.
+ *
+ * Applications are expected to launch the UI using the received {@link IntentSender} via
+ * {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}.
+ * </p>
+ *
+ * <p>
+ * Upon receiving user's confirmation Companion Device Manager Service will create an
+ * association and will send an {@link AssociationInfo} object that represents the created
+ * association back to the application both via
+ * {@link Callback#onAssociationCreated(AssociationInfo)} and
+ * via {@link Activity#setResult(int, Intent)}.
+ * In the latter the {@code resultCode} will be set to {@link Activity#RESULT_OK} and the
+ * {@code data} {@link Intent} will contain {@link AssociationInfo} extra named
+ * {@link #EXTRA_ASSOCIATION}.
+ * <pre>
+ * <code>
+ * if (resultCode == Activity.RESULT_OK) {
+ * AssociationInfo associationInfo = data.getParcelableExtra(EXTRA_ASSOCIATION);
+ * }
+ * </code>
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * If the Companion Device Manager Service is not able to create an association, it will
+ * invoke {@link Callback#onFailure(CharSequence)}.
+ *
+ * If this happened after the application has launched the UI (eg. the user chose to reject
+ * the association), the outcome will also be delivered to the applications via
+ * {@link Activity#setResult(int)} with the {@link Activity#RESULT_CANCELED}
+ * {@code resultCode}.
+ * </p>
+ *
+ * <p>
+ * Note that in some cases the Companion Device Manager Service may not need to collect
+ * user's approval for creating an association. In such cases, this method will not be
+ * invoked, and {@link #onAssociationCreated(AssociationInfo)} may be invoked right away.
+ * </p>
+ *
+ * @see #associate(AssociationRequest, Executor, Callback)
+ * @see #associate(AssociationRequest, Callback, Handler)
+ * @see #EXTRA_ASSOCIATION
*/
public abstract static class Callback {
+ /**
+ * @deprecated method was renamed to onAssociationPending() to provide better clarity; both
+ * methods are functionally equivalent and only one needs to be overridden.
+ *
+ * @see #onAssociationPending(IntentSender)
+ */
+ @Deprecated
+ public void onDeviceFound(@NonNull IntentSender intentSender) {}
/**
- * Called once at least one suitable device is found
+ * Invoked when the association needs to approved by the user.
*
- * @param chooserLauncher a {@link IntentSender} to launch the UI for user to select a
- * device
+ * Applications should launch the {@link Activity} "encapsulated" in {@code intentSender}
+ * {@link IntentSender} object by calling
+ * {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}.
+ *
+ * @param intentSender an {@link IntentSender} which applications should use to launch
+ * the UI for the user to confirm the association.
*/
- public abstract void onDeviceFound(IntentSender chooserLauncher);
+ public void onAssociationPending(@NonNull IntentSender intentSender) {
+ onDeviceFound(intentSender);
+ }
/**
- * Called if there was an error looking for device(s)
+ * Invoked when the association is created.
*
- * @param error the cause of the error
+ * @param associationInfo contains details of the newly-established association.
*/
- public abstract void onFailure(CharSequence error);
+ public void onAssociationCreated(@NonNull AssociationInfo associationInfo) {}
+
+ /**
+ * Invoked if the association could not be created.
+ *
+ * @param error error message.
+ */
+ public abstract void onFailure(@Nullable CharSequence error);
}
private final ICompanionDeviceManager mService;
private Context mContext;
+ @GuardedBy("mListeners")
+ private final ArrayList<OnAssociationsChangedListenerProxy> mListeners = new ArrayList<>();
+
/** @hide */
public CompanionDeviceManager(
@Nullable ICompanionDeviceManager service, @NonNull Context context) {
@@ -119,59 +230,109 @@
}
/**
- * Associate this app with a companion device, selected by user
+ * Request to associate this app with a companion device.
*
- * <p>Once at least one appropriate device is found, {@code callback} will be called with a
- * {@link PendingIntent} that can be used to show the list of available devices for the user
- * to select.
- * It should be started for result (i.e. using
- * {@link android.app.Activity#startIntentSenderForResult}), as the resulting
- * {@link android.content.Intent} will contain extra {@link #EXTRA_DEVICE}, with the selected
- * device. (e.g. {@link android.bluetooth.BluetoothDevice})</p>
+ * <p>Note that before creating establishing association the system may need to show UI to
+ * collect user confirmation.</p>
*
- * <p>If your app needs to be excluded from battery optimizations (run in the background)
- * or to have unrestricted data access (use data in the background) you can declare that
- * you use the {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} and {@link
- * android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND} respectively. Note that these
- * special capabilities have a negative effect on the device's battery and user's data
- * usage, therefore you should request them when absolutely necessary.</p>
+ * <p>If the app needs to be excluded from battery optimizations (run in the background)
+ * or to have unrestricted data access (use data in the background) it should declare use of
+ * {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} and
+ * {@link android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND} in its
+ * AndroidManifest.xml respectively.
+ * Note that these special capabilities have a negative effect on the device's battery and
+ * user's data usage, therefore you should request them when absolutely necessary.</p>
*
- * <p>You can call {@link #getAssociations} to get the list of currently associated
- * devices, and {@link #disassociate} to remove an association. Consider doing so when the
- * association is no longer relevant to avoid unnecessary battery and/or data drain resulting
- * from special privileges that the association provides</p>
+ * <p>Application can use {@link #getMyAssociations()} for retrieving the list of currently
+ * {@link AssociationInfo} objects, that represent their existing associations.
+ * Applications can also use {@link #disassociate(int)} to remove an association, and are
+ * recommended to do when an association is no longer relevant to avoid unnecessary battery
+ * and/or data drain resulting from special privileges that the association provides</p>
*
* <p>Calling this API requires a uses-feature
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
+ **
+ * @param request A request object that describes details of the request.
+ * @param callback The callback used to notify application when the association is created.
+ * @param handler The handler which will be used to invoke the callback.
*
- * <p>When using {@link AssociationRequest#DEVICE_PROFILE_WATCH watch}
- * {@link AssociationRequest.Builder#setDeviceProfile profile}, caller must also hold
- * {@link Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH}</p>
- *
- * @param request specific details about this request
- * @param callback will be called once there's at least one device found for user to choose from
- * @param handler A handler to control which thread the callback will be delivered on, or null,
- * to deliver it on main thread
- *
- * @see AssociationRequest
+ * @see AssociationRequest.Builder
+ * @see #getMyAssociations()
+ * @see #disassociate(int)
+ * @see #associate(AssociationRequest, Executor, Callback)
*/
- @RequiresPermission(
- value = Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH,
- conditional = true)
+ @UserHandleAware
+ @RequiresPermission(anyOf = {
+ REQUEST_COMPANION_PROFILE_WATCH,
+ REQUEST_COMPANION_PROFILE_APP_STREAMING,
+ REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION,
+ }, conditional = true)
public void associate(
@NonNull AssociationRequest request,
@NonNull Callback callback,
@Nullable Handler handler) {
- if (!checkFeaturePresent()) {
- return;
- }
+ if (!checkFeaturePresent()) return;
Objects.requireNonNull(request, "Request cannot be null");
Objects.requireNonNull(callback, "Callback cannot be null");
+ handler = Handler.mainIfNull(handler);
+
try {
- mService.associate(
- request,
- new CallbackProxy(request, callback, Handler.mainIfNull(handler)),
- getCallingPackage());
+ mService.associate(request, new AssociationRequestCallbackProxy(handler, callback),
+ mContext.getOpPackageName(), mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to associate this app with a companion device.
+ *
+ * <p>Note that before creating establishing association the system may need to show UI to
+ * collect user confirmation.</p>
+ *
+ * <p>If the app needs to be excluded from battery optimizations (run in the background)
+ * or to have unrestricted data access (use data in the background) it should declare use of
+ * {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} and
+ * {@link android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND} in its
+ * AndroidManifest.xml respectively.
+ * Note that these special capabilities have a negative effect on the device's battery and
+ * user's data usage, therefore you should request them when absolutely necessary.</p>
+ *
+ * <p>Application can use {@link #getMyAssociations()} for retrieving the list of currently
+ * {@link AssociationInfo} objects, that represent their existing associations.
+ * Applications can also use {@link #disassociate(int)} to remove an association, and are
+ * recommended to do when an association is no longer relevant to avoid unnecessary battery
+ * and/or data drain resulting from special privileges that the association provides</p>
+ *
+ * <p>Calling this API requires a uses-feature
+ * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
+ **
+ * @param request A request object that describes details of the request.
+ * @param executor The executor which will be used to invoke the callback.
+ * @param callback The callback used to notify application when the association is created.
+ *
+ * @see AssociationRequest.Builder
+ * @see #getMyAssociations()
+ * @see #disassociate(int)
+ */
+ @UserHandleAware
+ @RequiresPermission(anyOf = {
+ REQUEST_COMPANION_PROFILE_WATCH,
+ REQUEST_COMPANION_PROFILE_APP_STREAMING,
+ REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION
+ }, conditional = true)
+ public void associate(
+ @NonNull AssociationRequest request,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ if (!checkFeaturePresent()) return;
+ Objects.requireNonNull(request, "Request cannot be null");
+ Objects.requireNonNull(executor, "Executor cannot be null");
+ Objects.requireNonNull(callback, "Callback cannot be null");
+
+ try {
+ mService.associate(request, new AssociationRequestCallbackProxy(executor, callback),
+ mContext.getOpPackageName(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -182,15 +343,32 @@
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
*
* @return a list of MAC addresses of devices that have been previously associated with the
- * current app. You can use these with {@link #disassociate}
+ * current app are managed by CompanionDeviceManager (ie. does not include devices managed by
+ * application itself even if they have a MAC address).
+ *
+ * @deprecated use {@link #getMyAssociations()}
*/
+ @Deprecated
+ @UserHandleAware
@NonNull
public List<String> getAssociations() {
- if (!checkFeaturePresent()) {
- return Collections.emptyList();
- }
+ return CollectionUtils.mapNotNull(getMyAssociations(),
+ a -> a.isSelfManaged() ? null : a.getDeviceMacAddressAsString());
+ }
+
+ /**
+ * <p>Calling this API requires a uses-feature
+ * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
+ *
+ * @return a list of associations that have been previously associated with the current app.
+ */
+ @UserHandleAware
+ @NonNull
+ public List<AssociationInfo> getMyAssociations() {
+ if (!checkFeaturePresent()) return Collections.emptyList();
+
try {
- return mService.getAssociations(getCallingPackage(), mContext.getUserId());
+ return mService.getAssociations(mContext.getOpPackageName(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -209,13 +387,41 @@
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
*
* @param deviceMacAddress the MAC address of device to disassociate from this app
+ *
+ * @deprecated use {@link #disassociate(int)}
*/
+ @UserHandleAware
+ @Deprecated
public void disassociate(@NonNull String deviceMacAddress) {
- if (!checkFeaturePresent()) {
- return;
- }
+ if (!checkFeaturePresent()) return;
+
try {
- mService.disassociate(deviceMacAddress, getCallingPackage());
+ mService.legacyDisassociate(deviceMacAddress, mContext.getOpPackageName(),
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove an association.
+ *
+ * <p>Any privileges provided via being associated with a given device will be revoked</p>
+ *
+ * <p>Calling this API requires a uses-feature
+ * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
+ *
+ * @param associationId id of the association to be removed.
+ *
+ * @see #associate(AssociationRequest, Executor, Callback)
+ * @see AssociationInfo#getId()
+ */
+ @UserHandleAware
+ public void disassociate(int associationId) {
+ if (!checkFeaturePresent()) return;
+
+ try {
+ mService.disassociate(associationId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -234,12 +440,14 @@
* <p>Calling this API requires a uses-feature
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
*/
+ @UserHandleAware
public void requestNotificationAccess(ComponentName component) {
if (!checkFeaturePresent()) {
return;
}
try {
- IntentSender intentSender = mService.requestNotificationAccess(component)
+ IntentSender intentSender = mService
+ .requestNotificationAccess(component, mContext.getUserId())
.getIntentSender();
mContext.startIntentSender(intentSender, null, 0, 0, 0);
} catch (RemoteException e) {
@@ -304,9 +512,7 @@
@NonNull String packageName,
@NonNull MacAddress macAddress,
@NonNull UserHandle user) {
- if (!checkFeaturePresent()) {
- return false;
- }
+ if (!checkFeaturePresent()) return false;
Objects.requireNonNull(packageName, "package name cannot be null");
Objects.requireNonNull(macAddress, "mac address cannot be null");
Objects.requireNonNull(user, "user cannot be null");
@@ -322,21 +528,91 @@
* Gets all package-device {@link AssociationInfo}s for the current user.
*
* @return the associations list
+ * @see #addOnAssociationsChangedListener(Executor, OnAssociationsChangedListener)
+ * @see #removeOnAssociationsChangedListener(OnAssociationsChangedListener)
* @hide
*/
+ @SystemApi
+ @UserHandleAware
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public @NonNull List<AssociationInfo> getAllAssociations() {
- if (!checkFeaturePresent()) {
- return Collections.emptyList();
- }
+ if (!checkFeaturePresent()) return Collections.emptyList();
try {
- return mService.getAssociationsForUser(mContext.getUser().getIdentifier());
+ return mService.getAllAssociationsForUser(mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
+ * Listener for any changes to {@link AssociationInfo}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface OnAssociationsChangedListener {
+ /**
+ * Invoked when a change occurs to any of the associations for the user (including adding
+ * new associations and removing existing associations).
+ *
+ * @param associations all existing associations for the user (after the change).
+ */
+ void onAssociationsChanged(@NonNull List<AssociationInfo> associations);
+ }
+
+ /**
+ * Register listener for any changes to {@link AssociationInfo}.
+ *
+ * @see #getAllAssociations()
+ * @hide
+ */
+ @SystemApi
+ @UserHandleAware
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
+ public void addOnAssociationsChangedListener(
+ @NonNull Executor executor, @NonNull OnAssociationsChangedListener listener) {
+ if (!checkFeaturePresent()) return;
+ synchronized (mListeners) {
+ final OnAssociationsChangedListenerProxy proxy = new OnAssociationsChangedListenerProxy(
+ executor, listener);
+ try {
+ mService.addOnAssociationsChangedListener(proxy, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mListeners.add(proxy);
+ }
+ }
+
+ /**
+ * Unregister listener for any changes to {@link AssociationInfo}.
+ *
+ * @see #getAllAssociations()
+ * @hide
+ */
+ @SystemApi
+ @UserHandleAware
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
+ public void removeOnAssociationsChangedListener(
+ @NonNull OnAssociationsChangedListener listener) {
+ if (!checkFeaturePresent()) return;
+ synchronized (mListeners) {
+ final Iterator<OnAssociationsChangedListenerProxy> iterator = mListeners.iterator();
+ while (iterator.hasNext()) {
+ final OnAssociationsChangedListenerProxy proxy = iterator.next();
+ if (proxy.mListener == listener) {
+ try {
+ mService.removeOnAssociationsChangedListener(proxy, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ /**
* Checks whether the bluetooth device represented by the mac address was recently associated
* with the companion app. This allows these devices to skip the Bluetooth pairing dialog if
* their pairing variant is {@link BluetoothDevice#PAIRING_VARIANT_CONSENT}.
@@ -404,8 +680,8 @@
}
Objects.requireNonNull(deviceAddress, "address cannot be null");
try {
- mService.registerDevicePresenceListenerService(
- mContext.getPackageName(), deviceAddress);
+ mService.registerDevicePresenceListenerService(deviceAddress,
+ mContext.getOpPackageName(), mContext.getUserId());
} catch (RemoteException e) {
ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class);
throw e.rethrowFromSystemServer();
@@ -437,8 +713,8 @@
}
Objects.requireNonNull(deviceAddress, "address cannot be null");
try {
- mService.unregisterDevicePresenceListenerService(
- mContext.getPackageName(), deviceAddress);
+ mService.unregisterDevicePresenceListenerService(deviceAddress,
+ mContext.getPackageName(), mContext.getUserId());
} catch (RemoteException e) {
ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class);
}
@@ -509,78 +785,63 @@
return featurePresent;
}
- private Activity getActivity() {
- return (Activity) mContext;
- }
+ private static class AssociationRequestCallbackProxy extends IAssociationRequestCallback.Stub {
+ private final Handler mHandler;
+ private final Callback mCallback;
+ private final Executor mExecutor;
- private String getCallingPackage() {
- return mContext.getPackageName();
- }
-
- private class CallbackProxy extends IFindDeviceCallback.Stub
- implements Application.ActivityLifecycleCallbacks {
-
- private Callback mCallback;
- private Handler mHandler;
- private AssociationRequest mRequest;
-
- final Object mLock = new Object();
-
- private CallbackProxy(AssociationRequest request, Callback callback, Handler handler) {
+ private AssociationRequestCallbackProxy(
+ @NonNull Executor executor, @NonNull Callback callback) {
+ mExecutor = executor;
+ mHandler = null;
mCallback = callback;
+ }
+
+ private AssociationRequestCallbackProxy(
+ @NonNull Handler handler, @NonNull Callback callback) {
mHandler = handler;
- mRequest = request;
- getActivity().getApplication().registerActivityLifecycleCallbacks(this);
+ mExecutor = null;
+ mCallback = callback;
}
@Override
- public void onSuccess(PendingIntent launcher) {
- lockAndPost(Callback::onDeviceFound, launcher.getIntentSender());
+ public void onAssociationPending(@NonNull PendingIntent pi) {
+ execute(mCallback::onAssociationPending, pi.getIntentSender());
}
@Override
- public void onFailure(CharSequence reason) {
- lockAndPost(Callback::onFailure, reason);
+ public void onAssociationCreated(@NonNull AssociationInfo association) {
+ execute(mCallback::onAssociationCreated, association);
}
- <T> void lockAndPost(BiConsumer<Callback, T> action, T payload) {
- synchronized (mLock) {
- if (mHandler != null) {
- mHandler.post(() -> {
- Callback callback = null;
- synchronized (mLock) {
- callback = mCallback;
- }
- if (callback != null) {
- action.accept(callback, payload);
- }
- });
- }
+ @Override
+ public void onFailure(CharSequence error) throws RemoteException {
+ execute(mCallback::onFailure, error);
+ }
+
+ private <T> void execute(Consumer<T> callback, T arg) {
+ if (mExecutor != null) {
+ mExecutor.execute(() -> callback.accept(arg));
+ } else {
+ mHandler.post(() -> callback.accept(arg));
}
}
+ }
- @Override
- public void onActivityDestroyed(Activity activity) {
- synchronized (mLock) {
- if (activity != getActivity()) return;
- try {
- mService.stopScan(mRequest, this, getCallingPackage());
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- getActivity().getApplication().unregisterActivityLifecycleCallbacks(this);
- mCallback = null;
- mHandler = null;
- mRequest = null;
- mContext = null;
- }
+ private static class OnAssociationsChangedListenerProxy
+ extends IOnAssociationsChangedListener.Stub {
+ private final Executor mExecutor;
+ private final OnAssociationsChangedListener mListener;
+
+ private OnAssociationsChangedListenerProxy(Executor executor,
+ OnAssociationsChangedListener listener) {
+ mExecutor = executor;
+ mListener = listener;
}
- @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
- @Override public void onActivityStarted(Activity activity) {}
- @Override public void onActivityResumed(Activity activity) {}
- @Override public void onActivityPaused(Activity activity) {}
- @Override public void onActivityStopped(Activity activity) {}
- @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
+ @Override
+ public void onAssociationsChanged(@NonNull List<AssociationInfo> associations) {
+ mExecutor.execute(() -> mListener.onAssociationsChanged(associations));
+ }
}
}
diff --git a/core/java/android/companion/DeviceId.java b/core/java/android/companion/DeviceId.java
deleted file mode 100644
index 5deed1a..0000000
--- a/core/java/android/companion/DeviceId.java
+++ /dev/null
@@ -1,127 +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 android.companion;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * The class represents free-form ID of a companion device.
- *
- * Since companion devices may have multiple IDs of different type at the same time
- * (eg. a MAC address and a Serial Number), this class not only stores the ID itself, it also stores
- * the type of the ID.
- * Both the type of the ID and its actual value are represented as {@link String}-s.
- *
- * Examples of device IDs:
- * - "mac_address: f0:18:98:b3:fd:2e"
- * - "ip_address: 128.121.35.200"
- * - "imei: 352932100034923 / 44"
- * - "serial_number: 96141FFAZ000B7"
- * - "meid_hex: 35293210003492"
- * - "meid_dic: 08918 92240 0001 3548"
- *
- * @hide
- * TODO(b/1979395): un-hide when implementing public APIs that use this class.
- */
-public final class DeviceId implements Parcelable {
- public static final String TYPE_MAC_ADDRESS = "mac_address";
-
- private final @NonNull String mType;
- private final @NonNull String mValue;
-
- /**
- * @param type type of the ID. Non-empty. Max length - 16 characters.
- * @param value the ID. Non-empty. Max length - 48 characters.
- * @throws IllegalArgumentException if either {@param type} or {@param value} is empty or
- * exceeds its max allowed length.
- */
- public DeviceId(@NonNull String type, @NonNull String value) {
- if (type.isEmpty() || value.isEmpty()) {
- throw new IllegalArgumentException("'type' and 'value' should not be empty");
- }
- this.mType = type;
- this.mValue = value;
- }
-
- /**
- * @return the type of the ID.
- */
- public @NonNull String getType() {
- return mType;
- }
-
- /**
- * @return the ID.
- */
- public @NonNull String getValue() {
- return mValue;
- }
-
- @Override
- public String toString() {
- return "DeviceId{"
- + "type='" + mType + '\''
- + ", value='" + mValue + '\''
- + '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof DeviceId)) return false;
- DeviceId deviceId = (DeviceId) o;
- return Objects.equals(mType, deviceId.mType) && Objects.equals(mValue,
- deviceId.mValue);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mType, mValue);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(mType);
- dest.writeString(mValue);
- }
-
- private DeviceId(@NonNull Parcel in) {
- mType = in.readString();
- mValue = in.readString();
- }
-
- public static final @NonNull Creator<DeviceId> CREATOR = new Creator<DeviceId>() {
- @Override
- public DeviceId createFromParcel(@NonNull Parcel in) {
- return new DeviceId(in);
- }
-
- @Override
- public DeviceId[] newArray(int size) {
- return new DeviceId[size];
- }
- };
-}
diff --git a/core/java/android/companion/IFindDeviceCallback.aidl b/core/java/android/companion/IAssociationRequestCallback.aidl
similarity index 66%
copy from core/java/android/companion/IFindDeviceCallback.aidl
copy to core/java/android/companion/IAssociationRequestCallback.aidl
index a3a47a9..8cc2a71 100644
--- a/core/java/android/companion/IFindDeviceCallback.aidl
+++ b/core/java/android/companion/IAssociationRequestCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -17,10 +17,13 @@
package android.companion;
import android.app.PendingIntent;
+import android.companion.AssociationInfo;
/** @hide */
-interface IFindDeviceCallback {
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- oneway void onSuccess(in PendingIntent launcher);
- oneway void onFailure(in CharSequence reason);
-}
+interface IAssociationRequestCallback {
+ oneway void onAssociationPending(in PendingIntent pendingIntent);
+
+ oneway void onAssociationCreated(in AssociationInfo associationInfo);
+
+ oneway void onFailure(in CharSequence error);
+}
\ No newline at end of file
diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
index 71e5b24..702e8db 100644
--- a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
+++ b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
@@ -17,7 +17,7 @@
package android.companion;
import android.companion.AssociationRequest;
-import android.companion.IFindDeviceCallback;
+import android.companion.IAssociationRequestCallback;
import com.android.internal.infra.AndroidFuture;
@@ -26,7 +26,7 @@
void startDiscovery(
in AssociationRequest request,
in String callingPackage,
- in IFindDeviceCallback findCallback,
+ in IAssociationRequestCallback applicationCallback,
in AndroidFuture<String> serviceCallback);
void onAssociationCreated();
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 101f948..1558db2 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -17,7 +17,8 @@
package android.companion;
import android.app.PendingIntent;
-import android.companion.IFindDeviceCallback;
+import android.companion.IAssociationRequestCallback;
+import android.companion.IOnAssociationsChangedListener;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.content.ComponentName;
@@ -28,32 +29,41 @@
* @hide
*/
interface ICompanionDeviceManager {
- void associate(in AssociationRequest request,
- in IFindDeviceCallback callback,
- in String callingPackage);
- void stopScan(in AssociationRequest request,
- in IFindDeviceCallback callback,
- in String callingPackage);
+ void associate(in AssociationRequest request, in IAssociationRequestCallback callback,
+ in String callingPackage, int userId);
- List<String> getAssociations(String callingPackage, int userId);
- List<AssociationInfo> getAssociationsForUser(int userId);
+ List<AssociationInfo> getAssociations(String callingPackage, int userId);
+ List<AssociationInfo> getAllAssociationsForUser(int userId);
- void disassociate(String deviceMacAddress, String callingPackage);
+ /** @deprecated */
+ void legacyDisassociate(String deviceMacAddress, String callingPackage, int userId);
+ void disassociate(int associationId);
+
+ /** @deprecated */
boolean hasNotificationAccess(in ComponentName component);
- PendingIntent requestNotificationAccess(in ComponentName component);
+ PendingIntent requestNotificationAccess(in ComponentName component, int userId);
+
+ /** @deprecated */
boolean isDeviceAssociatedForWifiConnection(in String packageName, in String macAddress,
int userId);
- void registerDevicePresenceListenerService(in String packageName, in String deviceAddress);
+ void registerDevicePresenceListenerService(in String deviceAddress, in String callingPackage,
+ int userId);
- void unregisterDevicePresenceListenerService(in String packageName, in String deviceAddress);
+ void unregisterDevicePresenceListenerService(in String deviceAddress, in String callingPackage,
+ int userId);
+ /** @deprecated */
boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId);
+ /** @deprecated */
void createAssociation(in String packageName, in String macAddress, int userId,
in byte[] certificate);
void dispatchMessage(in int messageId, in int associationId, in byte[] message);
+
+ void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
+ void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
}
diff --git a/core/java/android/companion/IFindDeviceCallback.aidl b/core/java/android/companion/IOnAssociationsChangedListener.aidl
similarity index 67%
rename from core/java/android/companion/IFindDeviceCallback.aidl
rename to core/java/android/companion/IOnAssociationsChangedListener.aidl
index a3a47a9..e6794b7 100644
--- a/core/java/android/companion/IFindDeviceCallback.aidl
+++ b/core/java/android/companion/IOnAssociationsChangedListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,11 +16,9 @@
package android.companion;
-import android.app.PendingIntent;
+import android.companion.AssociationInfo;
/** @hide */
-interface IFindDeviceCallback {
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- oneway void onSuccess(in PendingIntent launcher);
- oneway void onFailure(in CharSequence reason);
-}
+interface IOnAssociationsChangedListener {
+ oneway void onAssociationsChanged(in List<AssociationInfo> associations);
+}
\ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a5a75a4..2f2151e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3004,8 +3004,8 @@
* {@link android.os.Build.VERSION_CODES#TIRAMISU},
* either {@link #RECEIVER_EXPORTED} or
* {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
- * for
- * <a href="https://developer.android.com/guide/components/broadcasts#system-broadcasts">system broadcasts</a> or an exception will be thrown. If
+ * for <a href="{@docRoot}guide/components/broadcasts#system-broadcasts">system
+ * broadcasts</a> or an exception will be thrown. If
* {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
* specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
* system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
@@ -3087,8 +3087,8 @@
* {@link android.os.Build.VERSION_CODES#TIRAMISU},
* either {@link #RECEIVER_EXPORTED} or
* {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
- * for
- * <a href="https://developer.android.com/guide/components/broadcasts#system-broadcasts">system broadcasts</a> or an exception will be thrown. If
+ * for <a href="{@docRoot}guide/components/broadcasts#system-broadcasts">system
+ * broadcasts</a> or an exception will be thrown. If
* {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
* specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
* system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
@@ -3157,8 +3157,8 @@
* {@link android.os.Build.VERSION_CODES#TIRAMISU},
* either {@link #RECEIVER_EXPORTED} or
* {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
- * for
- * <a href="https://developer.android.com/guide/components/broadcasts#system-broadcasts">system broadcasts</a> or an exception will be thrown. If
+ * for <a href="{@docRoot}guide/components/broadcasts#system-broadcasts">system
+ * broadcasts</a> or an exception will be thrown. If
* {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
* specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
* system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
@@ -3232,8 +3232,8 @@
* {@link android.os.Build.VERSION_CODES#TIRAMISU},
* either {@link #RECEIVER_EXPORTED} or
* {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
- * for
- * <a href="https://developer.android.com/guide/components/broadcasts#system-broadcasts">system broadcasts</a> or an exception will be thrown. If
+ * for <a href="{@docRoot}guide/components/broadcasts#system-broadcasts">system
+ * broadcasts</a> or an exception will be thrown. If
* {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
* specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
* system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
@@ -3842,6 +3842,8 @@
UWB_SERVICE,
MEDIA_METRICS_SERVICE,
SUPPLEMENTAL_PROCESS_SERVICE,
+ //@hide: ATTESTATION_VERIFICATION_SERVICE,
+ //@hide: SAFETY_CENTER_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -5354,9 +5356,12 @@
* {@link android.net.NetworkScoreManager} for managing network scoring.
* @see #getSystemService(String)
* @see android.net.NetworkScoreManager
+ * @deprecated see https://developer.android.com/guide/topics/connectivity/wifi-suggest for
+ * alternative API to propose WiFi networks.
* @hide
*/
@SystemApi
+ @Deprecated
public static final String NETWORK_SCORE_SERVICE = "network_score";
/**
@@ -5739,6 +5744,15 @@
/**
* Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.security.attestationverification.AttestationVerificationManager}.
+ * @see #getSystemService(String)
+ * @see android.security.attestationverification.AttestationVerificationManager
+ * @hide
+ */
+ public static final String ATTESTATION_VERIFICATION_SERVICE = "attestation_verification";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve an
* {@link android.security.FileIntegrityManager}.
* @see #getSystemService(String)
* @see android.security.FileIntegrityManager
@@ -5871,6 +5885,27 @@
public static final String SUPPLEMENTAL_PROCESS_SERVICE = "supplemental_process";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
+ * android.safetycenter.SafetyCenterManager} instance for interacting with the safety center.
+ *
+ * @see #getSystemService(String)
+ * @see android.safetycenter.SafetyCenterManager
+ * @hide
+ */
+ @SystemApi
+ public static final String SAFETY_CENTER_SERVICE = "safety_center";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.nearby.NearbyManager} to discover nearby devices.
+ *
+ * @see #getSystemService(String)
+ * @see android.nearby.NearbyManager
+ * @hide
+ */
+ public static final String NEARBY_SERVICE = "nearby";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -6544,30 +6579,24 @@
@NonNull Configuration overrideConfiguration);
/**
- * Return a new Context object for the current Context but whose resources
- * are adjusted to match the metrics of the given Display. Each call to this method
- * returns a new instance of a Context object; Context objects are not
- * shared, however common state (ClassLoader, other Resources for the
- * same configuration) may be so the Context itself can be fairly lightweight.
- *
- * To obtain an instance of a {@link WindowManager} (see {@link #getSystemService(String)}) that
- * is configured to show windows on the given display call
- * {@link #createWindowContext(int, Bundle)} on the returned display Context or use an
- * {@link android.app.Activity}.
- *
+ * Returns a new <code>Context</code> object from the current context but with resources
+ * adjusted to match the metrics of <code>display</code>. Each call to this method
+ * returns a new instance of a context object. Context objects are not shared; however,
+ * common state (such as the {@link ClassLoader} and other resources for the same
+ * configuration) can be shared, so the <code>Context</code> itself is lightweight.
* <p>
- * Note that invoking #createDisplayContext(Display) from an UI context is not regarded
- * as an UI context. In other words, it is not suggested to access UI components (such as
- * obtain a {@link WindowManager} by {@link #getSystemService(String)})
- * from the context created from #createDisplayContext(Display).
- * </p>
+ * To obtain an instance of {@link WindowManager} configured to show windows on the given
+ * display, call {@link #createWindowContext(int, Bundle)} on the returned display context,
+ * then call {@link #getSystemService(String)} or {@link #getSystemService(Class)} on the
+ * returned window context.
+ * <p>
+ * <b>Note:</b> The context returned by <code>createDisplayContext(Display)</code> is not a UI
+ * context. Do not access UI components or obtain a {@link WindowManager} from the context
+ * created by <code>createDisplayContext(Display)</code>.
*
- * @param display A {@link Display} object specifying the display for whose metrics the
- * Context's resources should be tailored.
+ * @param display The display to which the current context's resources are adjusted.
*
- * @return A {@link Context} for the display.
- *
- * @see #getSystemService(String)
+ * @return A context for the display.
*/
@DisplayContext
public abstract Context createDisplayContext(@NonNull Display display);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a3efbd7..a36d532 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2378,6 +2378,14 @@
public static final String ACTION_VIEW_APP_FEATURES =
"android.intent.action.VIEW_APP_FEATURES";
+ /**
+ * Activity action: Launch UI to open the Safety Center, which highlights the user's security
+ * and privacy status.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SAFETY_CENTER =
+ "android.intent.action.SAFETY_CENTER";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent broadcast actions (see action variable).
@@ -5522,8 +5530,8 @@
/**
* A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD},
- * that specifies whether the system displayed attribution information in the
- * permission usage system UI for the chosen entry.
+ * that specifies whether the permission usage system UI is showing attribution information
+ * for the chosen entry.
*
* <p> The extra can only be true if application has specified attributionsAreUserVisible
* in its manifest. </p>
@@ -6916,7 +6924,6 @@
*/
public static final int FLAG_RECEIVER_OFFLOAD = 0x80000000;
/**
- /**
* If set, when sending a broadcast the recipient will run on the system dedicated queue.
*
* @hide
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index f72288c..18e205f 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -18,6 +18,7 @@
import android.content.pm.Checksum;
import android.content.pm.DataLoaderParamsParcel;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageInstallObserver2;
import android.content.IntentSender;
import android.os.ParcelFileDescriptor;
@@ -36,6 +37,7 @@
void stageViaHardLink(String target);
void setChecksums(String name, in Checksum[] checksums, in byte[] signature);
+ void requestChecksums(in String name, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener);
void removeSplit(String splitName);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 46f1797..1c82b38 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -69,41 +69,34 @@
void checkPackageStartable(String packageName, int userId);
@UnsupportedAppUsage(trackingBug = 171933273)
boolean isPackageAvailable(String packageName, int userId);
- @UnsupportedAppUsage
- PackageInfo getPackageInfo(String packageName, int flags, int userId);
+ PackageInfo getPackageInfo(String packageName, long flags, int userId);
PackageInfo getPackageInfoVersioned(in VersionedPackage versionedPackage,
- int flags, int userId);
- @UnsupportedAppUsage
- int getPackageUid(String packageName, int flags, int userId);
- int[] getPackageGids(String packageName, int flags, int userId);
+ long flags, int userId);
+ int getPackageUid(String packageName, long flags, int userId);
+ int[] getPackageGids(String packageName, long flags, int userId);
@UnsupportedAppUsage
String[] currentToCanonicalPackageNames(in String[] names);
@UnsupportedAppUsage
String[] canonicalToCurrentPackageNames(in String[] names);
- @UnsupportedAppUsage
- ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
+ ApplicationInfo getApplicationInfo(String packageName, long flags, int userId);
/**
* @return the target SDK for the given package name, or -1 if it cannot be retrieved
*/
int getTargetSdkVersion(String packageName);
- @UnsupportedAppUsage
- ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
+ ActivityInfo getActivityInfo(in ComponentName className, long flags, int userId);
boolean activitySupportsIntent(in ComponentName className, in Intent intent,
String resolvedType);
- @UnsupportedAppUsage
- ActivityInfo getReceiverInfo(in ComponentName className, int flags, int userId);
+ ActivityInfo getReceiverInfo(in ComponentName className, long flags, int userId);
- @UnsupportedAppUsage
- ServiceInfo getServiceInfo(in ComponentName className, int flags, int userId);
+ ServiceInfo getServiceInfo(in ComponentName className, long flags, int userId);
- @UnsupportedAppUsage
- ProviderInfo getProviderInfo(in ComponentName className, int flags, int userId);
+ ProviderInfo getProviderInfo(in ComponentName className, long flags, int userId);
boolean isProtectedBroadcast(String actionName);
@@ -133,33 +126,31 @@
@UnsupportedAppUsage
boolean isUidPrivileged(int uid);
- @UnsupportedAppUsage
- ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
+ ResolveInfo resolveIntent(in Intent intent, String resolvedType, long flags, int userId);
ResolveInfo findPersistentPreferredActivity(in Intent intent, int userId);
boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
ParceledListSlice queryIntentActivities(in Intent intent,
- String resolvedType, int flags, int userId);
+ String resolvedType, long flags, int userId);
ParceledListSlice queryIntentActivityOptions(
in ComponentName caller, in Intent[] specifics,
in String[] specificTypes, in Intent intent,
- String resolvedType, int flags, int userId);
+ String resolvedType, long flags, int userId);
ParceledListSlice queryIntentReceivers(in Intent intent,
- String resolvedType, int flags, int userId);
+ String resolvedType, long flags, int userId);
ResolveInfo resolveService(in Intent intent,
- String resolvedType, int flags, int userId);
+ String resolvedType, long flags, int userId);
ParceledListSlice queryIntentServices(in Intent intent,
- String resolvedType, int flags, int userId);
+ String resolvedType, long flags, int userId);
ParceledListSlice queryIntentContentProviders(in Intent intent,
- String resolvedType, int flags, int userId);
+ String resolvedType, long flags, int userId);
/**
* This implements getInstalledPackages via a "last returned row"
@@ -167,8 +158,7 @@
* limit that kicks in when flags are included that bloat up the data
* returned.
*/
- @UnsupportedAppUsage
- ParceledListSlice getInstalledPackages(int flags, in int userId);
+ ParceledListSlice getInstalledPackages(long flags, in int userId);
/**
* This implements getPackagesHoldingPermissions via a "last returned row"
@@ -177,7 +167,7 @@
* returned.
*/
ParceledListSlice getPackagesHoldingPermissions(in String[] permissions,
- int flags, int userId);
+ long flags, int userId);
/**
* This implements getInstalledApplications via a "last returned row"
@@ -185,18 +175,17 @@
* limit that kicks in when flags are included that bloat up the data
* returned.
*/
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- ParceledListSlice getInstalledApplications(int flags, int userId);
+ ParceledListSlice getInstalledApplications(long flags, int userId);
/**
* Retrieve all applications that are marked as persistent.
*
- * @return A List<applicationInfo> containing one entry for each persistent
+ * @return A List<ApplicationInfo> containing one entry for each persistent
* application.
*/
ParceledListSlice getPersistentApplications(int flags);
- ProviderInfo resolveContentProvider(String name, int flags, int userId);
+ ProviderInfo resolveContentProvider(String name, long flags, int userId);
/**
* Retrieve sync information for all content providers.
@@ -211,7 +200,7 @@
inout List<ProviderInfo> outInfo);
ParceledListSlice queryContentProviders(
- String processName, int uid, int flags, String metaDataKey);
+ String processName, int uid, long flags, String metaDataKey);
@UnsupportedAppUsage
InstrumentationInfo getInstrumentationInfo(
@@ -690,9 +679,9 @@
int getInstallReason(String packageName, int userId);
- ParceledListSlice getSharedLibraries(in String packageName, int flags, int userId);
+ ParceledListSlice getSharedLibraries(in String packageName, long flags, int userId);
- ParceledListSlice getDeclaredSharedLibraries(in String packageName, int flags, int userId);
+ ParceledListSlice getDeclaredSharedLibraries(in String packageName, long flags, int userId);
boolean canRequestPackageInstalls(String packageName, int userId);
@@ -750,7 +739,7 @@
void notifyPackagesReplacedReceived(in String[] packages);
- void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
+ void requestPackageChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
String featureId, int userId);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1586013..80584d1 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -19,6 +19,13 @@
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.Checksum.TYPE_WHOLE_MD5;
+import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
import android.Manifest;
import android.annotation.CurrentTimeMillisLong;
@@ -62,12 +69,16 @@
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.Closeable;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.MessageDigest;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -406,6 +417,13 @@
@Retention(RetentionPolicy.SOURCE)
public @interface FileLocation{}
+ /** Default set of checksums - includes all available checksums.
+ * @see Session#requestChecksums */
+ private static final int DEFAULT_CHECKSUMS =
+ TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 | TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA1 | TYPE_WHOLE_SHA256
+ | TYPE_WHOLE_SHA512 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256
+ | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
+
private final IPackageInstaller mInstaller;
private final int mUserId;
private final String mInstallerPackageName;
@@ -1256,7 +1274,7 @@
* @param name previously written as part of this session.
* {@link #openWrite}
* @param checksums installer intends to make available via
- * {@link PackageManager#requestChecksums}.
+ * {@link PackageManager#requestChecksums} or {@link #requestChecksums}.
* @param signature DER PKCS#7 detached signature bytes over binary serialized checksums
* to enable integrity checking for the checksums or null for no integrity
* checking. {@link PackageManager#requestChecksums} will return
@@ -1293,6 +1311,89 @@
}
}
+ private static List<byte[]> encodeCertificates(List<Certificate> certs) throws
+ CertificateEncodingException {
+ if (certs == null) {
+ return null;
+ }
+ List<byte[]> result = new ArrayList<>(certs.size());
+ for (Certificate cert : certs) {
+ if (!(cert instanceof X509Certificate)) {
+ throw new CertificateEncodingException("Only X509 certificates supported.");
+ }
+ result.add(cert.getEncoded());
+ }
+ return result;
+ }
+
+ /**
+ * Requests checksums for the APK file in session.
+ * <p>
+ * A possible use case is replying to {@link Intent#ACTION_PACKAGE_NEEDS_VERIFICATION}
+ * broadcast.
+ * The checksums will be returned asynchronously via onChecksumsReadyListener.
+ * <p>
+ * By default returns all readily available checksums:
+ * <ul>
+ * <li>enforced by platform,
+ * <li>enforced by the installer.
+ * </ul>
+ * If the caller needs a specific checksum type, they can specify it as required.
+ * <p>
+ * <b>Caution: Android can not verify installer-provided checksums. Make sure you specify
+ * trusted installers.</b>
+ * <p>
+ * @param name previously written as part of this session.
+ * {@link #openWrite}
+ * @param required to explicitly request the checksum types. Will incur significant
+ * CPU/memory/disk usage.
+ * @param trustedInstallers for checksums enforced by installer, which installers are to be
+ * trusted.
+ * {@link PackageManager#TRUST_ALL} will return checksums from any
+ * installer,
+ * {@link PackageManager#TRUST_NONE} disables optimized
+ * installer-enforced checksums, otherwise the list has to be
+ * a non-empty list of certificates.
+ * @param onChecksumsReadyListener called once when the results are available.
+ * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
+ * @throws FileNotFoundException if the file does not exist.
+ * @throws IllegalArgumentException if the list of trusted installer certificates is empty.
+ */
+ public void requestChecksums(@NonNull String name, @Checksum.TypeMask int required,
+ @NonNull List<Certificate> trustedInstallers,
+ @NonNull PackageManager.OnChecksumsReadyListener onChecksumsReadyListener)
+ throws CertificateEncodingException, FileNotFoundException {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(onChecksumsReadyListener);
+ Objects.requireNonNull(trustedInstallers);
+ if (trustedInstallers == PackageManager.TRUST_ALL) {
+ trustedInstallers = null;
+ } else if (trustedInstallers == PackageManager.TRUST_NONE) {
+ trustedInstallers = Collections.emptyList();
+ } else if (trustedInstallers.isEmpty()) {
+ throw new IllegalArgumentException(
+ "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+ + "list of certificates.");
+ }
+ try {
+ IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
+ new IOnChecksumsReadyListener.Stub() {
+ @Override
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ onChecksumsReadyListener.onChecksumsReady(checksums);
+ }
+ };
+ mSession.requestChecksums(name, DEFAULT_CHECKSUMS, required,
+ encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate);
+ } catch (ParcelableException e) {
+ e.maybeRethrow(FileNotFoundException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Attempt to commit everything staged in this session. This may require
* user intervention, and so it may not happen immediately. The final
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1b51314..a7f3801 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -697,7 +697,7 @@
MATCH_DISABLED_COMPONENTS,
MATCH_DISABLED_UNTIL_USED_COMPONENTS,
MATCH_INSTANT,
- MATCH_STATIC_SHARED_LIBRARIES,
+ MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
@@ -721,7 +721,7 @@
MATCH_SYSTEM_ONLY,
MATCH_UNINSTALLED_PACKAGES,
MATCH_INSTANT,
- MATCH_STATIC_SHARED_LIBRARIES,
+ MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
@@ -1038,14 +1038,14 @@
public static final int MATCH_EXPLICITLY_VISIBLE_ONLY = 0x02000000;
/**
- * Internal {@link PackageInfo} flag: include static shared libraries.
- * Apps that depend on static shared libs can always access the version
+ * Internal {@link PackageInfo} flag: include static shared and SDK libraries.
+ * Apps that depend on static shared/SDK libs can always access the version
* of the lib they depend on. System/shell/root can access all shared
* libs regardless of dependency but need to explicitly ask for them
* via this flag.
* @hide
*/
- public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
+ public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 0x04000000;
/**
* {@link PackageInfo} flag: return the signing certificates associated with
@@ -9044,7 +9044,7 @@
}
/**
- * Requesting the checksums for APKs within a package.
+ * Requests the checksums for APKs within a package.
* The checksums will be returned asynchronously via onChecksumsReadyListener.
*
* By default returns all readily available checksums:
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
index d157768..ff80e61 100644
--- a/core/java/android/content/pm/PackagePartitions.java
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -19,8 +19,11 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Build;
+import android.os.Build.Partition;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.SystemProperties;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,20 +67,34 @@
*/
private static final ArrayList<SystemPartition> SYSTEM_PARTITIONS =
new ArrayList<>(Arrays.asList(
- new SystemPartition(Environment.getRootDirectory(), PARTITION_SYSTEM,
+ new SystemPartition(Environment.getRootDirectory(),
+ PARTITION_SYSTEM, Partition.PARTITION_NAME_SYSTEM,
true /* containsPrivApp */, false /* containsOverlay */),
- new SystemPartition(Environment.getVendorDirectory(), PARTITION_VENDOR,
+ new SystemPartition(Environment.getVendorDirectory(),
+ PARTITION_VENDOR, Partition.PARTITION_NAME_VENDOR,
true /* containsPrivApp */, true /* containsOverlay */),
- new SystemPartition(Environment.getOdmDirectory(), PARTITION_ODM,
+ new SystemPartition(Environment.getOdmDirectory(),
+ PARTITION_ODM, Partition.PARTITION_NAME_ODM,
true /* containsPrivApp */, true /* containsOverlay */),
- new SystemPartition(Environment.getOemDirectory(), PARTITION_OEM,
+ new SystemPartition(Environment.getOemDirectory(),
+ PARTITION_OEM, Partition.PARTITION_NAME_OEM,
false /* containsPrivApp */, true /* containsOverlay */),
- new SystemPartition(Environment.getProductDirectory(), PARTITION_PRODUCT,
+ new SystemPartition(Environment.getProductDirectory(),
+ PARTITION_PRODUCT, Partition.PARTITION_NAME_PRODUCT,
true /* containsPrivApp */, true /* containsOverlay */),
- new SystemPartition(Environment.getSystemExtDirectory(), PARTITION_SYSTEM_EXT,
+ new SystemPartition(Environment.getSystemExtDirectory(),
+ PARTITION_SYSTEM_EXT, Partition.PARTITION_NAME_SYSTEM_EXT,
true /* containsPrivApp */, true /* containsOverlay */)));
/**
+ * A string to represent the fingerprint of this build and all package partitions. Using it to
+ * determine whether the system update has occurred. Different from {@link Build#FINGERPRINT},
+ * this string is digested from the fingerprints of the build and all package partitions to
+ * help detect the partition update.
+ */
+ public static final String FINGERPRINT = getFingerprint();
+
+ /**
* Returns a list in which the elements are products of the specified function applied to the
* list of {@link #SYSTEM_PARTITIONS} in increasing specificity order.
*/
@@ -101,6 +118,23 @@
}
}
+ /**
+ * Returns a fingerprint string for this build and all package partitions. The string is
+ * digested from the fingerprints of the build and all package partitions.
+ *
+ * @return A string to represent the fingerprint of this build and all package partitions.
+ */
+ @NonNull
+ private static String getFingerprint() {
+ final String[] digestProperties = new String[SYSTEM_PARTITIONS.size() + 1];
+ for (int i = 0; i < SYSTEM_PARTITIONS.size(); i++) {
+ final String partitionName = SYSTEM_PARTITIONS.get(i).getName();
+ digestProperties[i] = "ro." + partitionName + ".build.fingerprint";
+ }
+ digestProperties[SYSTEM_PARTITIONS.size()] = "ro.build.fingerprint"; // build fingerprint
+ return SystemProperties.digestOf(digestProperties);
+ }
+
/** Represents a partition that contains application packages. */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public static class SystemPartition {
@@ -108,6 +142,9 @@
public final int type;
@NonNull
+ private final String mName;
+
+ @NonNull
private final DeferredCanonicalFile mFolder;
@Nullable
@@ -122,9 +159,10 @@
@NonNull
private final File mNonConicalFolder;
- private SystemPartition(@NonNull File folder, @PartitionType int type,
+ private SystemPartition(@NonNull File folder, @PartitionType int type, String name,
boolean containsPrivApp, boolean containsOverlay) {
this.type = type;
+ this.mName = name;
this.mFolder = new DeferredCanonicalFile(folder);
this.mAppFolder = new DeferredCanonicalFile(folder, "app");
this.mPrivAppFolder = containsPrivApp ? new DeferredCanonicalFile(folder, "priv-app")
@@ -136,6 +174,7 @@
public SystemPartition(@NonNull SystemPartition original) {
this.type = original.type;
+ this.mName = original.mName;
this.mFolder = new DeferredCanonicalFile(original.mFolder.getFile());
this.mAppFolder = original.mAppFolder;
this.mPrivAppFolder = original.mPrivAppFolder;
@@ -148,10 +187,19 @@
* different root folder.
*/
public SystemPartition(@NonNull File rootFolder, @NonNull SystemPartition partition) {
- this(rootFolder, partition.type, partition.mPrivAppFolder != null,
+ this(rootFolder, partition.type, partition.mName, partition.mPrivAppFolder != null,
partition.mOverlayFolder != null);
}
+ /**
+ * Returns the name identifying the partition.
+ * @see Partition
+ */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
/** Returns the canonical folder of the partition. */
@NonNull
public File getFolder() {
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 7abb694..4ba2ee6 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -70,6 +70,13 @@
public static final int TYPE_STATIC = 2;
/**
+ * SDK library type: this library is <strong>not</strong> backwards
+ * -compatible, can be updated and updates can be uninstalled. Clients
+ * depend on a specific version of the library.
+ */
+ public static final int TYPE_SDK = 3;
+
+ /**
* Constant for referring to an undefined version.
*/
public static final int VERSION_UNDEFINED = -1;
@@ -289,6 +296,13 @@
}
/**
+ * @hide
+ */
+ public boolean isSdk() {
+ return mType == TYPE_SDK;
+ }
+
+ /**
* Gets the package that declares the library.
*
* @return The package declaring the library.
@@ -351,6 +365,9 @@
case TYPE_STATIC: {
return "static";
}
+ case TYPE_SDK: {
+ return "sdk";
+ }
default: {
return "unknown";
}
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index cc4782a..26f0826 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -1,6 +1,9 @@
{
"imports": [
{
+ "path": "frameworks/base/core/tests/coretests/src/android/content/pm"
+ },
+ {
"path": "frameworks/base/services/tests/PackageManagerServiceTests"
},
{
@@ -10,6 +13,9 @@
"path": "frameworks/base/services/tests/PackageManagerComponentOverrideTests"
},
{
+ "path": "frameworks/base/services/tests/servicestests/src/com/android/server/pm"
+ },
+ {
"path": "cts/tests/tests/packageinstaller"
},
{
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index ef124c7..b11b38a 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -76,7 +76,7 @@
@Nullable
public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
- @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
Set<String> grantedPermissions, FrameworkPackageUserState state, int userId) {
return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions,
state, userId, null);
@@ -90,7 +90,7 @@
@Nullable
private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids,
- @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
@Nullable ApexInfo apexInfo) {
ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
@@ -190,7 +190,7 @@
@Nullable
public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids,
- @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
@Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
if (!checkUseInstalled(pkg, state, flags)) {
@@ -210,9 +210,9 @@
*/
@NonNull
public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids,
- @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
- @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
+ @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+ long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
+ int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
PackageInfo pi = new PackageInfo();
pi.packageName = pkg.getPackageName();
pi.splitNames = pkg.getSplitNames();
@@ -365,7 +365,8 @@
@Nullable
public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg,
- @PackageManager.ApplicationInfoFlags int flags, FrameworkPackageUserState state, int userId) {
+ @PackageManager.ApplicationInfoFlags long flags, FrameworkPackageUserState state,
+ int userId) {
if (pkg == null) {
return null;
}
@@ -392,8 +393,8 @@
*/
@NonNull
public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
- @PackageManager.ApplicationInfoFlags int flags, @NonNull FrameworkPackageUserState state,
- int userId, boolean assignUserFields) {
+ @PackageManager.ApplicationInfoFlags long flags,
+ @NonNull FrameworkPackageUserState state, int userId, boolean assignUserFields) {
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = ((ParsingPackageHidden) pkg).toAppInfoWithoutState();
@@ -406,7 +407,7 @@
return ai;
}
- private static void updateApplicationInfo(ApplicationInfo ai, int flags,
+ private static void updateApplicationInfo(ApplicationInfo ai, long flags,
FrameworkPackageUserState state) {
if ((flags & PackageManager.GET_META_DATA) == 0) {
ai.metaData = null;
@@ -452,8 +453,8 @@
@Nullable
public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
- @PackageManager.ApplicationInfoFlags int flags, @NonNull FrameworkPackageUserState state,
- int userId) {
+ @PackageManager.ApplicationInfoFlags long flags,
+ @NonNull FrameworkPackageUserState state, int userId) {
if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
return null;
}
@@ -469,7 +470,7 @@
@Nullable
public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
- @PackageManager.ComponentInfoFlags int flags, @NonNull FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlags long flags, @NonNull FrameworkPackageUserState state,
int userId) {
if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
return null;
@@ -484,7 +485,7 @@
@Nullable
public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (a == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -504,12 +505,12 @@
* This bypasses critical checks that are necessary for usage with data passed outside of system
* server.
* <p>
- * Prefer {@link #generateActivityInfo(ParsingPackageRead, ParsedActivity, int,
+ * Prefer {@link #generateActivityInfo(ParsingPackageRead, ParsedActivity, long,
* FrameworkPackageUserState, ApplicationInfo, int)}.
*/
@NonNull
public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags,
+ @PackageManager.ComponentInfoFlags long flags,
@NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo();
@@ -550,13 +551,14 @@
@Nullable
public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state, int userId) {
+ @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ int userId) {
return generateActivityInfo(pkg, a, flags, state, null, userId);
}
@Nullable
public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (s == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -576,12 +578,12 @@
* This bypasses critical checks that are necessary for usage with data passed outside of system
* server.
* <p>
- * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, int, FrameworkPackageUserState,
- * ApplicationInfo, int)}.
+ * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, long,
+ * FrameworkPackageUserState, ApplicationInfo, int)}.
*/
@NonNull
public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s,
- @PackageManager.ComponentInfoFlags int flags,
+ @PackageManager.ComponentInfoFlags long flags,
@NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo();
@@ -600,13 +602,14 @@
@Nullable
public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state, int userId) {
+ @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ int userId) {
return generateServiceInfo(pkg, s, flags, state, null, userId);
}
@Nullable
public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state,
+ @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (p == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -626,12 +629,12 @@
* This bypasses critical checks that are necessary for usage with data passed outside of system
* server.
* <p>
- * Prefer {@link #generateProviderInfo(ParsingPackageRead, ParsedProvider, int,
+ * Prefer {@link #generateProviderInfo(ParsingPackageRead, ParsedProvider, long,
* FrameworkPackageUserState, ApplicationInfo, int)}.
*/
@NonNull
public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags,
+ @PackageManager.ComponentInfoFlags long flags,
@NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ProviderInfo pi = new ProviderInfo();
@@ -661,17 +664,18 @@
@Nullable
public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state, int userId) {
+ @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+ int userId) {
return generateProviderInfo(pkg, p, flags, state, null, userId);
}
/**
- * @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead, int,
- * FrameworkPackageUserState, int, boolean)}
+ * @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead,
+ * long, FrameworkPackageUserState, int, boolean)}
*/
@Nullable
public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
- ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId,
+ ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags long flags, int userId,
boolean assignUserFields) {
if (i == null) return null;
@@ -702,7 +706,7 @@
@Nullable
public static PermissionInfo generatePermissionInfo(ParsedPermission p,
- @PackageManager.ComponentInfoFlags int flags) {
+ @PackageManager.ComponentInfoFlags long flags) {
if (p == null) return null;
PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
@@ -725,7 +729,7 @@
@Nullable
public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
- @PackageManager.ComponentInfoFlags int flags) {
+ @PackageManager.ComponentInfoFlags long flags) {
if (pg == null) return null;
PermissionGroupInfo pgi = new PermissionGroupInfo(
@@ -753,8 +757,8 @@
return new Attribution(pa.getTag(), pa.getLabel());
}
- private static boolean checkUseInstalledOrHidden(int flags, @NonNull FrameworkPackageUserState state,
- @Nullable ApplicationInfo appInfo) {
+ private static boolean checkUseInstalledOrHidden(long flags,
+ @NonNull FrameworkPackageUserState state, @Nullable ApplicationInfo appInfo) {
// Returns false if the package is hidden system app until installed.
if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
&& !state.isInstalled()
@@ -882,8 +886,8 @@
return privateFlagsExt;
}
- private static boolean checkUseInstalled(ParsingPackageRead pkg, FrameworkPackageUserState state,
- @PackageManager.PackageInfoFlags int flags) {
+ private static boolean checkUseInstalled(ParsingPackageRead pkg,
+ FrameworkPackageUserState state, @PackageManager.PackageInfoFlags long flags) {
// If available for the target user
return PackageUserStateUtils.isAvailable(state, flags);
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 056f99f..63332e7 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -103,11 +103,11 @@
ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
- ParsingPackage addUsesStaticLibrary(String libraryName);
+ ParsingPackage addUsesSdkLibrary(String libraryName, long versionMajor,
+ String[] certSha256Digests);
- ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
-
- ParsingPackage addUsesStaticLibraryVersion(long version);
+ ParsingPackage addUsesStaticLibrary(String libraryName, long version,
+ String[] certSha256Digests);
ParsingPackage addQueriesIntent(Intent intent);
@@ -212,6 +212,12 @@
ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
+ ParsingPackage setSdkLibName(String sdkLibName);
+
+ ParsingPackage setSdkLibVersionMajor(int sdkLibVersionMajor);
+
+ ParsingPackage setSdkLibrary(boolean sdkLibrary);
+
ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode);
ParsingPackage setStaticSharedLibrary(boolean staticSharedLibrary);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f07f382..19a8ce9 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -179,6 +179,10 @@
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
+ private String sdkLibName;
+ private int sdkLibVersionMajor;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
private String staticSharedLibName;
private long staticSharedLibVersion;
@NonNull
@@ -203,10 +207,17 @@
private List<String> usesStaticLibraries = emptyList();
@Nullable
private long[] usesStaticLibrariesVersions;
-
@Nullable
private String[][] usesStaticLibrariesCertDigests;
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ private List<String> usesSdkLibraries = emptyList();
+ @Nullable
+ private long[] usesSdkLibrariesVersionsMajor;
+ @Nullable
+ private String[][] usesSdkLibrariesCertDigests;
+
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String sharedUserId;
@@ -334,7 +345,7 @@
@DataClass.ParcelWith(ForInternedString.class)
private String backupAgentName;
private int banner;
- private int category;
+ private int category = ApplicationInfo.CATEGORY_UNDEFINED;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String classLoaderName;
@@ -518,6 +529,7 @@
private static final long REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1L << 46;
private static final long ATTRIBUTIONS_ARE_USER_VISIBLE = 1L << 47;
private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48;
+ private static final long SDK_LIBRARY = 1L << 49;
}
private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
@@ -828,21 +840,24 @@
}
@Override
- public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
+ public ParsingPackageImpl addUsesSdkLibrary(String libraryName, long versionMajor,
+ String[] certSha256Digests) {
+ this.usesSdkLibraries = CollectionUtils.add(this.usesSdkLibraries,
+ TextUtils.safeIntern(libraryName));
+ this.usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
+ this.usesSdkLibrariesVersionsMajor, versionMajor, true);
+ this.usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+ this.usesSdkLibrariesCertDigests, certSha256Digests, true);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addUsesStaticLibrary(String libraryName, long version,
+ String[] certSha256Digests) {
this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
TextUtils.safeIntern(libraryName));
- return this;
- }
-
- @Override
- public ParsingPackageImpl addUsesStaticLibraryVersion(long version) {
this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions,
version, true);
- return this;
- }
-
- @Override
- public ParsingPackageImpl addUsesStaticLibraryCertDigests(String[] certSha256Digests) {
this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
this.usesStaticLibrariesCertDigests, certSha256Digests, true);
return this;
@@ -1136,6 +1151,8 @@
dest.writeString(this.overlayCategory);
dest.writeInt(this.overlayPriority);
sForInternedStringValueMap.parcel(this.overlayables, dest, flags);
+ sForInternedString.parcel(this.sdkLibName, dest, flags);
+ dest.writeInt(this.sdkLibVersionMajor);
sForInternedString.parcel(this.staticSharedLibName, dest, flags);
dest.writeLong(this.staticSharedLibVersion);
sForInternedStringList.parcel(this.libraryNames, dest, flags);
@@ -1143,9 +1160,9 @@
sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
+
sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
dest.writeLongArray(this.usesStaticLibrariesVersions);
-
if (this.usesStaticLibrariesCertDigests == null) {
dest.writeInt(-1);
} else {
@@ -1155,6 +1172,17 @@
}
}
+ sForInternedStringList.parcel(this.usesSdkLibraries, dest, flags);
+ dest.writeLongArray(this.usesSdkLibrariesVersionsMajor);
+ if (this.usesSdkLibrariesCertDigests == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(this.usesSdkLibrariesCertDigests.length);
+ for (int index = 0; index < this.usesSdkLibrariesCertDigests.length; index++) {
+ dest.writeStringArray(this.usesSdkLibrariesCertDigests[index]);
+ }
+ }
+
sForInternedString.parcel(this.sharedUserId, dest, flags);
dest.writeInt(this.sharedUserLabel);
dest.writeTypedList(this.configPreferences);
@@ -1259,6 +1287,8 @@
this.overlayCategory = in.readString();
this.overlayPriority = in.readInt();
this.overlayables = sForInternedStringValueMap.unparcel(in);
+ this.sdkLibName = sForInternedString.unparcel(in);
+ this.sdkLibVersionMajor = in.readInt();
this.staticSharedLibName = sForInternedString.unparcel(in);
this.staticSharedLibVersion = in.readLong();
this.libraryNames = sForInternedStringList.unparcel(in);
@@ -1266,14 +1296,29 @@
this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
this.usesNativeLibraries = sForInternedStringList.unparcel(in);
this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
+
this.usesStaticLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibrariesVersions = in.createLongArray();
+ {
+ int digestsSize = in.readInt();
+ if (digestsSize >= 0) {
+ this.usesStaticLibrariesCertDigests = new String[digestsSize][];
+ for (int index = 0; index < digestsSize; index++) {
+ this.usesStaticLibrariesCertDigests[index] = sForInternedStringArray.unparcel(
+ in);
+ }
+ }
+ }
- int digestsSize = in.readInt();
- if (digestsSize >= 0) {
- this.usesStaticLibrariesCertDigests = new String[digestsSize][];
- for (int index = 0; index < digestsSize; index++) {
- this.usesStaticLibrariesCertDigests[index] = sForInternedStringArray.unparcel(in);
+ this.usesSdkLibraries = sForInternedStringList.unparcel(in);
+ this.usesSdkLibrariesVersionsMajor = in.createLongArray();
+ {
+ int digestsSize = in.readInt();
+ if (digestsSize >= 0) {
+ this.usesSdkLibrariesCertDigests = new String[digestsSize][];
+ for (int index = 0; index < digestsSize; index++) {
+ this.usesSdkLibrariesCertDigests[index] = sForInternedStringArray.unparcel(in);
+ }
}
}
@@ -1479,6 +1524,17 @@
@Nullable
@Override
+ public String getSdkLibName() {
+ return sdkLibName;
+ }
+
+ @Override
+ public int getSdkLibVersionMajor() {
+ return sdkLibVersionMajor;
+ }
+
+ @Nullable
+ @Override
public String getStaticSharedLibName() {
return staticSharedLibName;
}
@@ -1536,6 +1592,18 @@
return usesStaticLibrariesCertDigests;
}
+ @NonNull
+ @Override
+ public List<String> getUsesSdkLibraries() { return usesSdkLibraries; }
+
+ @Nullable
+ @Override
+ public long[] getUsesSdkLibrariesVersionsMajor() { return usesSdkLibrariesVersionsMajor; }
+
+ @Nullable
+ @Override
+ public String[][] getUsesSdkLibrariesCertDigests() { return usesSdkLibrariesCertDigests; }
+
@Nullable
@Override
public String getSharedUserId() {
@@ -2083,6 +2151,11 @@
}
@Override
+ public boolean isSdkLibrary() {
+ return getBoolean(Booleans.SDK_LIBRARY);
+ }
+
+ @Override
public boolean isOverlay() {
return getBoolean(Booleans.OVERLAY);
}
@@ -2558,6 +2631,23 @@
}
@Override
+ public ParsingPackageImpl setSdkLibName(String sdkLibName) {
+ this.sdkLibName = TextUtils.safeIntern(sdkLibName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSdkLibVersionMajor(int sdkLibVersionMajor) {
+ this.sdkLibVersionMajor = sdkLibVersionMajor;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSdkLibrary(boolean value) {
+ return setBoolean(Booleans.SDK_LIBRARY, value);
+ }
+
+ @Override
public ParsingPackageImpl setStaticSharedLibrary(boolean value) {
return setBoolean(Booleans.STATIC_SHARED_LIBRARY, value);
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 2933f95..49b3b08 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -196,6 +196,17 @@
int[] getSplitFlags();
/**
+ * @see R.styleable#AndroidManifestSdkLibrary_name
+ */
+ @Nullable
+ String getSdkLibName();
+
+ /**
+ * @see R.styleable#AndroidManifestSdkLibrary_versionMajor
+ */
+ int getSdkLibVersionMajor();
+
+ /**
* @see R.styleable#AndroidManifestStaticLibrary_name
*/
@Nullable
@@ -267,6 +278,26 @@
@Nullable
long[] getUsesStaticLibrariesVersions();
+ /**
+ * TODO(b/135203078): Move SDK library stuff to an inner data class
+ *
+ * @see R.styleable#AndroidManifestUsesSdkLibrary
+ */
+ @NonNull
+ List<String> getUsesSdkLibraries();
+
+ /**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary_certDigest
+ */
+ @Nullable
+ String[][] getUsesSdkLibrariesCertDigests();
+
+ /**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
+ */
+ @Nullable
+ long[] getUsesSdkLibrariesVersionsMajor();
+
boolean hasPreserveLegacyExternalStorage();
/**
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index d2ac8739..3e537c8 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -117,6 +117,7 @@
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
+import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -848,6 +849,8 @@
pkg.addProperty(propertyResult.getResult());
}
return propertyResult;
+ case "uses-sdk-library":
+ return parseUsesSdkLibrary(input, pkg, res, parser);
case "uses-static-library":
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
@@ -2212,7 +2215,8 @@
}
}
- if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
+ if (TextUtils.isEmpty(pkg.getStaticSharedLibName()) && TextUtils.isEmpty(
+ pkg.getSdkLibName())) {
// Add a hidden app detail activity to normal apps which forwards user to App Details
// page.
ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
@@ -2351,10 +2355,14 @@
pkg.addProperty(propertyResult.getResult());
}
return propertyResult;
+ case "sdk-library":
+ return parseSdkLibrary(pkg, res, parser, input);
case "static-library":
return parseStaticLibrary(pkg, res, parser, input);
case "library":
return parseLibrary(pkg, res, parser, input);
+ case "uses-sdk-library":
+ return parseUsesSdkLibrary(input, pkg, res, parser);
case "uses-static-library":
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
@@ -2375,6 +2383,41 @@
}
@NonNull
+ private static ParseResult<ParsingPackage> parseSdkLibrary(
+ ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, ParseInput input) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSdkLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestSdkLibrary_name);
+ final int versionMajor = sa.getInt(
+ R.styleable.AndroidManifestSdkLibrary_versionMajor,
+ -1);
+
+ // Fail if malformed.
+ if (lname == null || versionMajor < 0) {
+ return input.error("Bad sdk-library declaration name: " + lname
+ + " version: " + versionMajor);
+ } else if (pkg.getSharedUserId() != null) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+ "sharedUserId not allowed in SDK library"
+ );
+ } else if (pkg.getSdkLibName() != null) {
+ return input.error("Multiple SDKs for package "
+ + pkg.getPackageName());
+ }
+
+ return input.success(pkg.setSdkLibName(lname.intern())
+ .setSdkLibVersionMajor(versionMajor)
+ .setSdkLibrary(true));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
private static ParseResult<ParsingPackage> parseStaticLibrary(
ParsingPackage pkg, Resources res,
XmlResourceParser parser, ParseInput input) {
@@ -2436,6 +2479,68 @@
}
@NonNull
+ private static ParseResult<ParsingPackage> parseUsesSdkLibrary(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdkLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestUsesSdkLibrary_name);
+ final int versionMajor = sa.getInt(
+ R.styleable.AndroidManifestUsesSdkLibrary_versionMajor, -1);
+ String certSha256Digest = sa.getNonResourceString(R.styleable
+ .AndroidManifestUsesSdkLibrary_certDigest);
+
+ // Since an APK providing a static shared lib can only provide the lib - fail if
+ // malformed
+ if (lname == null || versionMajor < 0 || certSha256Digest == null) {
+ return input.error("Bad uses-sdk-library declaration name: " + lname
+ + " version: " + versionMajor + " certDigest" + certSha256Digest);
+ }
+
+ // Can depend only on one version of the same library
+ List<String> usesSdkLibraries = pkg.getUsesSdkLibraries();
+ if (usesSdkLibraries.contains(lname)) {
+ return input.error(
+ "Depending on multiple versions of SDK library " + lname);
+ }
+
+ lname = lname.intern();
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+
+ if ("".equals(certSha256Digest)) {
+ // Test-only uses-sdk-library empty certificate digest override.
+ certSha256Digest = SystemProperties.get(
+ "debug.pm.uses_sdk_library_default_cert_digest", "");
+ // Validate the overridden digest.
+ try {
+ HexEncoding.decode(certSha256Digest, false);
+ } catch (IllegalArgumentException e) {
+ certSha256Digest = "";
+ }
+ }
+
+ ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+ if (certResult.isError()) {
+ return input.error(certResult);
+ }
+ String[] additionalCertSha256Digests = certResult.getResult();
+
+ final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
+ certSha256Digests[0] = certSha256Digest;
+ System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+ 1, additionalCertSha256Digests.length);
+
+ return input.success(pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
@@ -2483,9 +2588,7 @@
System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
1, additionalCertSha256Digests.length);
- return input.success(pkg.addUsesStaticLibrary(lname)
- .addUsesStaticLibraryVersion(version)
- .addUsesStaticLibraryCertDigests(certSha256Digests));
+ return input.success(pkg.addUsesStaticLibrary(lname, version, certSha256Digests));
} finally {
sa.recycle();
}
diff --git a/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java b/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java
index fcad10c..625b9d1 100644
--- a/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java
+++ b/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java
@@ -21,8 +21,6 @@
import android.content.pm.ApplicationInfo;
import android.util.SparseArray;
-import com.android.internal.R;
-
/**
* Container for fields that are eventually exposed through {@link ApplicationInfo}.
* <p>
@@ -576,6 +574,11 @@
boolean isStaticSharedLibrary();
/**
+ * True means that this package/app contains an SDK library.
+ */
+ boolean isSdkLibrary();
+
+ /**
* If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
* android.os.Build.VERSION_CODES#GINGERBREAD}.
*
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
index 1ac9739..0334601 100644
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -170,13 +170,13 @@
}
public static boolean isMatch(FrameworkPackageUserState state, boolean isSystem,
- boolean isPackageEnabled, ParsedMainComponent component, int flags) {
+ boolean isPackageEnabled, ParsedMainComponent component, long flags) {
return PackageUserStateUtils.isMatch(state, isSystem, isPackageEnabled,
component.isEnabled(), component.isDirectBootAware(), component.getName(), flags);
}
public static boolean isEnabled(FrameworkPackageUserState state, boolean isPackageEnabled,
- ParsedMainComponent parsedComponent, int flags) {
+ ParsedMainComponent parsedComponent, long flags) {
return PackageUserStateUtils.isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
parsedComponent.getName(), flags);
}
diff --git a/core/java/android/content/pm/pkg/PackageUserStateUtils.java b/core/java/android/content/pm/pkg/PackageUserStateUtils.java
index 9a800b0..468bff1 100644
--- a/core/java/android/content/pm/pkg/PackageUserStateUtils.java
+++ b/core/java/android/content/pm/pkg/PackageUserStateUtils.java
@@ -34,15 +34,15 @@
private static final boolean DEBUG = false;
private static final String TAG = "PackageUserStateUtils";
- public static boolean isMatch(@NonNull FrameworkPackageUserState state, ComponentInfo componentInfo,
- int flags) {
+ public static boolean isMatch(@NonNull FrameworkPackageUserState state,
+ ComponentInfo componentInfo, long flags) {
return isMatch(state, componentInfo.applicationInfo.isSystemApp(),
componentInfo.applicationInfo.enabled, componentInfo.enabled,
componentInfo.directBootAware, componentInfo.name, flags);
}
public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
- boolean isPackageEnabled, ParsedMainComponent component, int flags) {
+ boolean isPackageEnabled, ParsedMainComponent component, long flags) {
return isMatch(state, isSystem, isPackageEnabled, component.isEnabled(),
component.isDirectBootAware(), component.getName(), flags);
}
@@ -58,7 +58,7 @@
*/
public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
boolean isPackageEnabled, boolean isComponentEnabled,
- boolean isComponentDirectBootAware, String componentName, int flags) {
+ boolean isComponentDirectBootAware, String componentName, long flags) {
final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
if (!isAvailable(state, flags) && !(isSystem && matchUninstalled)) {
return reportIfDebug(false, flags);
@@ -81,7 +81,7 @@
return reportIfDebug(matchesUnaware || matchesAware, flags);
}
- public static boolean isAvailable(@NonNull FrameworkPackageUserState state, int flags) {
+ public static boolean isAvailable(@NonNull FrameworkPackageUserState state, long flags) {
// True if it is installed for this user and it is not hidden. If it is hidden,
// still return true if the caller requested MATCH_UNINSTALLED_PACKAGES
final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0;
@@ -91,7 +91,7 @@
&& (!state.isHidden() || matchUninstalled));
}
- public static boolean reportIfDebug(boolean result, int flags) {
+ public static boolean reportIfDebug(boolean result, long flags) {
if (DEBUG && !result) {
Slog.i(TAG, "No match!; flags: "
+ DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
@@ -101,13 +101,13 @@
}
public static boolean isEnabled(@NonNull FrameworkPackageUserState state, ComponentInfo componentInfo,
- int flags) {
+ long flags) {
return isEnabled(state, componentInfo.applicationInfo.enabled, componentInfo.enabled,
componentInfo.name, flags);
}
public static boolean isEnabled(@NonNull FrameworkPackageUserState state, boolean isPackageEnabled,
- ParsedMainComponent parsedComponent, int flags) {
+ ParsedMainComponent parsedComponent, long flags) {
return isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
parsedComponent.getName(), flags);
}
@@ -115,8 +115,9 @@
/**
* Test if the given component is considered enabled.
*/
- public static boolean isEnabled(@NonNull FrameworkPackageUserState state, boolean isPackageEnabled,
- boolean isComponentEnabled, String componentName, int flags) {
+ public static boolean isEnabled(@NonNull FrameworkPackageUserState state,
+ boolean isPackageEnabled, boolean isComponentEnabled, String componentName,
+ long flags) {
if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
return true;
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 8ebb8ec..01bf49e 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2432,27 +2432,10 @@
break;
}
- switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) {
- case Configuration.UI_MODE_TYPE_APPLIANCE:
- parts.add("appliance");
- break;
- case Configuration.UI_MODE_TYPE_DESK:
- parts.add("desk");
- break;
- case Configuration.UI_MODE_TYPE_TELEVISION:
- parts.add("television");
- break;
- case Configuration.UI_MODE_TYPE_CAR:
- parts.add("car");
- break;
- case Configuration.UI_MODE_TYPE_WATCH:
- parts.add("watch");
- break;
- case Configuration.UI_MODE_TYPE_VR_HEADSET:
- parts.add("vrheadset");
- break;
- default:
- break;
+ final String uiModeTypeString =
+ getUiModeTypeString(config.uiMode & Configuration.UI_MODE_TYPE_MASK);
+ if (uiModeTypeString != null) {
+ parts.add(uiModeTypeString);
}
switch (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) {
@@ -2587,6 +2570,28 @@
}
/**
+ * @hide
+ */
+ public static String getUiModeTypeString(int uiModeType) {
+ switch (uiModeType) {
+ case Configuration.UI_MODE_TYPE_APPLIANCE:
+ return "appliance";
+ case Configuration.UI_MODE_TYPE_DESK:
+ return "desk";
+ case Configuration.UI_MODE_TYPE_TELEVISION:
+ return "television";
+ case Configuration.UI_MODE_TYPE_CAR:
+ return "car";
+ case Configuration.UI_MODE_TYPE_WATCH:
+ return "watch";
+ case Configuration.UI_MODE_TYPE_VR_HEADSET:
+ return "vrheadset";
+ default:
+ return null;
+ }
+ }
+
+ /**
* Generate a delta Configuration between <code>base</code> and <code>change</code>. The
* resulting delta can be used with {@link #updateFrom(Configuration)}.
* <p />
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
index 2b4e4a1..95f13b5 100644
--- a/core/java/android/hardware/OWNERS
+++ b/core/java/android/hardware/OWNERS
@@ -6,3 +6,7 @@
# Sensors framework
per-file *Sensor*,*Trigger* = file:platform/frameworks/native:/services/sensorservice/OWNERS
+
+# Buffers
+per-file HardwareBuffer* = file:/graphics/java/android/graphics/OWNERS
+per-file DataSpace* = file:/graphics/java/android/graphics/OWNERS
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 96a18dc..3c8b6e9 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1323,7 +1323,7 @@
* <p>Maximum flashlight brightness level.</p>
* <p>If this value is greater than 1, then the device supports controlling the
* flashlight brightness level via
- * {android.hardware.camera2.CameraManager#setTorchStrengthLevel}.
+ * {android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel}.
* If this value is equal to 1, flashlight brightness control is not supported.
* The value for this key will be null for devices with no flash unit.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
@@ -1335,7 +1335,7 @@
/**
* <p>Default flashlight brightness level to be set via
- * {android.hardware.camera2.CameraManager#setTorchStrengthLevel}.</p>
+ * {android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel}.</p>
* <p>If flash unit is available this will be greater than or equal to 1 and less
* or equal to <code>{@link CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL android.flash.info.strengthMaximumLevel}</code>.</p>
* <p>Setting flashlight brightness above the default level
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 8864939..9b19fc4 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -110,6 +110,11 @@
private int mRepeatingRequestId = REQUEST_ID_NONE;
// Latest repeating request list's types
private int[] mRepeatingRequestTypes;
+
+ // Cache failed requests to process later in case of a repeating error callback
+ private int mFailedRepeatingRequestId = REQUEST_ID_NONE;
+ private int[] mFailedRepeatingRequestTypes;
+
// Map stream IDs to input/output configurations
private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
new SimpleEntry<>(REQUEST_ID_NONE, null);
@@ -1326,16 +1331,25 @@
int requestId = mRepeatingRequestId;
mRepeatingRequestId = REQUEST_ID_NONE;
+ mFailedRepeatingRequestId = REQUEST_ID_NONE;
int[] requestTypes = mRepeatingRequestTypes;
mRepeatingRequestTypes = null;
+ mFailedRepeatingRequestTypes = null;
long lastFrameNumber;
try {
lastFrameNumber = mRemoteDevice.cancelRequest(requestId);
} catch (IllegalArgumentException e) {
if (DEBUG) {
- Log.v(TAG, "Repeating request was already stopped for request " + requestId);
+ Log.v(TAG, "Repeating request was already stopped for request " +
+ requestId);
}
+ // Cache request id and request types in case of a race with
+ // "onRepeatingRequestError" which may no yet be scheduled on another thread
+ // or blocked by us.
+ mFailedRepeatingRequestId = requestId;
+ mFailedRepeatingRequestTypes = requestTypes;
+
// Repeating request was already stopped. Nothing more to do.
return;
}
@@ -1965,7 +1979,17 @@
synchronized(mInterfaceLock) {
// Camera is already closed or no repeating request is present.
if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) {
- return; // Camera already closed
+ if ((mFailedRepeatingRequestId == repeatingRequestId) &&
+ (mFailedRepeatingRequestTypes != null) && (mRemoteDevice != null)) {
+ Log.v(TAG, "Resuming stop of failed repeating request with id: " +
+ mFailedRepeatingRequestId);
+
+ checkEarlyTriggerSequenceCompleteLocked(mFailedRepeatingRequestId,
+ lastFrameNumber, mFailedRepeatingRequestTypes);
+ mFailedRepeatingRequestId = REQUEST_ID_NONE;
+ mFailedRepeatingRequestTypes = null;
+ }
+ return;
}
// Redirect device callback to the offline session in case we are in the middle
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 7d71984..0b1ed65 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -109,7 +109,8 @@
/** {@hide} */
public boolean quickPickupSensorEnabled(int user) {
- return !TextUtils.isEmpty(quickPickupSensorType())
+ return boolSettingDefaultOn(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, user)
+ && !TextUtils.isEmpty(quickPickupSensorType())
&& pickupGestureEnabled(user)
&& !alwaysOnEnabled(user);
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 4481885..4c81f9c 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -344,6 +344,16 @@
*/
public static final int VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP = 1 << 11;
+ /**
+ * Virtual display flags: Indicates that the virtual display should always be unlocked and not
+ * have keyguard displayed on it. Only valid for virtual displays that aren't in the default
+ * display group.
+ *
+ * @see #createVirtualDisplay
+ * @see #VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP
+ * @hide
+ */
+ public static final int VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED = 1 << 12;
/** @hide */
@IntDef(prefix = {"MATCH_CONTENT_FRAMERATE_"}, value = {
@@ -1363,5 +1373,13 @@
* @hide
*/
String KEY_HIGH_REFRESH_RATE_BLACKLIST = "high_refresh_rate_blacklist";
+
+ /**
+ * Whether to allow the creation of always unlocked virtual displays by apps having the
+ * required permissions.
+ * @hide
+ */
+ String KEY_ALLOW_ALWAYS_UNLOCKED_VIRTUAL_DISPLAYS =
+ "allow_always_unlocked_virtual_displays";
}
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index fd34fa4..2985c75 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.SensorManager;
+import android.media.projection.IMediaProjection;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
@@ -30,6 +31,7 @@
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.window.DisplayWindowPolicyController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -379,6 +381,39 @@
public abstract void onEarlyInteractivityChange(boolean interactive);
/**
+ * A special API for creates a virtual display with a DisplayPolicyController in system_server.
+ * <p>
+ * If this method is called without original calling uid, the caller must enforce the
+ * corresponding permissions according to the flags.
+ * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT}
+ * {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT}
+ * {@link android.Manifest.permission#ADD_TRUSTED_DISPLAY}
+ * {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW}
+ * </p>
+ *
+ * @param virtualDisplayConfig The arguments for the virtual display configuration. See
+ * {@link VirtualDisplayConfig} for using it.
+ * @param callback Callback to call when the virtual display's state changes, or null if none.
+ * @param projection MediaProjection token.
+ * @param packageName The package name of the app.
+ * @param controller The DisplayWindowPolicyControl that can control what contents are
+ * allowed to be displayed.
+ * @return The newly created virtual display id , or {@link Display#INVALID_DISPLAY} if the
+ * virtual display cannot be created.
+ */
+ public abstract int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
+ IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
+ DisplayWindowPolicyController controller);
+
+ /**
+ * Get {@link DisplayWindowPolicyController} associated to the {@link DisplayInfo#displayId}
+ *
+ * @param displayId The id of the display.
+ * @return The associated {@link DisplayWindowPolicyController}.
+ */
+ public abstract DisplayWindowPolicyController getDisplayWindowPolicyController(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/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index e5d8620..6f0c944 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1016,9 +1016,8 @@
}
/**
- * Queries the framework about whether any physical keys exist on the
- * any keyboard attached to the device that are capable of producing the given
- * array of key codes.
+ * Queries the framework about whether any physical keys exist on any currently attached input
+ * devices that are capable of producing the given array of key codes.
*
* @param keyCodes The array of key codes to query.
* @return A new array of the same size as the key codes array whose elements
@@ -1032,11 +1031,10 @@
}
/**
- * Queries the framework about whether any physical keys exist on the
- * any keyboard attached to the device that are capable of producing the given
- * array of key codes.
+ * Queries the framework about whether any physical keys exist on the specified input device
+ * that are capable of producing the given array of key codes.
*
- * @param id The id of the device to query.
+ * @param id The id of the input device to query or -1 to consult all devices.
* @param keyCodes The array of key codes to query.
* @return A new array of the same size as the key codes array whose elements are set to true
* if the given device could produce the corresponding key code at the same index in the key
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 4ecd15e..75beacf 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -68,6 +68,16 @@
private InputMethod mInputMethod;
/**
+ * @return {@link InputMethod} instance returned from {@link #onCreateInputMethodInterface()}.
+ * {@code null} if {@link #onCreateInputMethodInterface()} is not yet called.
+ * @hide
+ */
+ @Nullable
+ protected final InputMethod getInputMethodInternal() {
+ return mInputMethod;
+ }
+
+ /**
* Keep the strong reference to {@link InputMethodServiceInternal} to ensure that it will not be
* garbage-collected until {@link AbstractInputMethodService} gets garbage-collected.
*
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 5a517ee..eccbb40 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -32,11 +32,13 @@
import android.view.MotionEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.InputMethodSession;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodSession;
class IInputMethodSessionWrapper extends IInputMethodSession.Stub
@@ -54,6 +56,7 @@
private static final int DO_NOTIFY_IME_HIDDEN = 120;
private static final int DO_REMOVE_IME_SURFACE = 130;
private static final int DO_FINISH_INPUT = 140;
+ private static final int DO_INVALIDATE_INPUT = 150;
@UnsupportedAppUsage
@@ -142,6 +145,16 @@
mInputMethodSession.finishInput();
return;
}
+ case DO_INVALIDATE_INPUT: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mInputMethodSession.invalidateInputInternal((EditorInfo) args.arg1,
+ (IInputContext) args.arg2, msg.arg1);
+ } finally {
+ args.recycle();
+ }
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -218,6 +231,12 @@
}
@Override
+ public void invalidateInput(EditorInfo editorInfo, IInputContext inputContext, int sessionId) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(
+ DO_INVALIDATE_INPUT, sessionId, editorInfo, inputContext));
+ }
+
+ @Override
public void finishInput() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index aa3c361..22b444e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -135,6 +135,7 @@
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInputContext;
import com.android.internal.view.InlineSuggestionsRequestInfo;
import java.io.FileDescriptor;
@@ -394,7 +395,7 @@
/**
* @hide
- * The IME is visible.
+ * The IME is perceptibly visible to the user.
*/
public static final int IME_VISIBLE = 0x2;
@@ -405,6 +406,15 @@
*/
public static final int IME_INVISIBLE = 0x4;
+ /**
+ * @hide
+ * The IME is visible, but not yet perceptible to the user (e.g. fading in)
+ * by {@link android.view.WindowInsetsController}.
+ *
+ * @see InputMethodManager#reportPerceptible
+ */
+ public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x8;
+
// Min and max values for back disposition.
private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
@@ -1063,8 +1073,30 @@
public final void removeImeSurface() {
InputMethodService.this.scheduleImeSurfaceRemoval();
}
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public final void invalidateInputInternal(@NonNull EditorInfo editorInfo,
+ @NonNull IInputContext inputContext, int sessionId) {
+ if (mStartedInputConnection instanceof RemoteInputConnection) {
+ final RemoteInputConnection ric = (RemoteInputConnection) mStartedInputConnection;
+ if (!ric.isSameConnection(inputContext)) {
+ // This is not an error, and can be safely ignored.
+ if (DEBUG) {
+ Log.d(TAG, "ignoring invalidateInput() due to context mismatch.");
+ }
+ return;
+ }
+ editorInfo.makeCompatible(getApplicationInfo().targetSdkVersion);
+ getInputMethodInternal().restartInput(new RemoteInputConnection(ric, sessionId),
+ editorInfo);
+ }
+ }
}
-
+
/**
* Information about where interesting parts of the input method UI appear.
*/
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index ed617af..9ef2579 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -103,6 +103,17 @@
mCancellationGroup = cancellationGroup;
}
+ @AnyThread
+ public boolean isSameConnection(@NonNull IInputContext inputContext) {
+ return mInvoker.isSameConnection(inputContext);
+ }
+
+ RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) {
+ mImsInternal = original.mImsInternal;
+ mInvoker = original.mInvoker.cloneWithSessionId(sessionId);
+ mCancellationGroup = original.mCancellationGroup;
+ }
+
/**
* See {@link InputConnection#getTextAfterCursor(int, int)}.
*/
diff --git a/core/java/android/net/NetworkKey.java b/core/java/android/net/NetworkKey.java
index 5cd4eb5..5e0e1b7 100644
--- a/core/java/android/net/NetworkKey.java
+++ b/core/java/android/net/NetworkKey.java
@@ -35,8 +35,10 @@
/**
* Information which identifies a specific network.
*
+ * @deprecated as part of the {@link NetworkScoreManager} deprecation.
* @hide
*/
+@Deprecated
@SystemApi
// NOTE: Ideally, we would abstract away the details of what identifies a network of a specific
// type, so that all networks appear the same and can be scored without concern to the network type
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 0ba2663..7b8b5c0 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -51,9 +51,13 @@
* permission.
* </ul>
*
+ * @deprecated No longer functional on {@link android.os.Build.VERSION_CODES#TIRAMISU} and above.
+ * See https://developer.android.com/guide/topics/connectivity/wifi-suggest for
+ * alternative APIs to suggest/configure Wi-Fi networks.
* @hide
*/
@SystemApi
+@Deprecated
@SystemService(Context.NETWORK_SCORE_SERVICE)
public class NetworkScoreManager {
private static final String TAG = "NetworkScoreManager";
@@ -245,7 +249,7 @@
* or {@link permission#REQUEST_NETWORK_SCORES} permissions.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.SCORE_NETWORKS,
- android.Manifest.permission.REQUEST_NETWORK_SCORES})
+ android.Manifest.permission.REQUEST_NETWORK_SCORES})
public String getActiveScorerPackage() {
try {
return mService.getActiveScorerPackage();
@@ -322,7 +326,7 @@
* hold the {@link permission#REQUEST_NETWORK_SCORES} permission.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.SCORE_NETWORKS,
- android.Manifest.permission.REQUEST_NETWORK_SCORES})
+ android.Manifest.permission.REQUEST_NETWORK_SCORES})
public boolean clearScores() throws SecurityException {
try {
return mService.clearScores();
@@ -344,7 +348,7 @@
*/
@SystemApi
@RequiresPermission(anyOf = {android.Manifest.permission.SCORE_NETWORKS,
- android.Manifest.permission.REQUEST_NETWORK_SCORES})
+ android.Manifest.permission.REQUEST_NETWORK_SCORES})
public boolean setActiveScorer(String packageName) throws SecurityException {
try {
return mService.setActiveScorer(packageName);
@@ -362,7 +366,7 @@
* hold the {@link permission#REQUEST_NETWORK_SCORES} permission.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.SCORE_NETWORKS,
- android.Manifest.permission.REQUEST_NETWORK_SCORES})
+ android.Manifest.permission.REQUEST_NETWORK_SCORES})
public void disableScoring() throws SecurityException {
try {
mService.disableScoring();
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index ee24084..c906a13 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -70,9 +70,17 @@
private static final String TAG = "NetworkTemplate";
/**
+ * Initial Version of the backup serializer.
+ */
+ public static final int BACKUP_VERSION_1_INIT = 1;
+ /**
+ * Version of the backup serializer that added carrier template support.
+ */
+ public static final int BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE = 2;
+ /**
* Current Version of the Backup Serializer.
*/
- private static final int BACKUP_VERSION = 1;
+ private static final int BACKUP_VERSION = BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE;
public static final int MATCH_MOBILE = 1;
public static final int MATCH_WIFI = 4;
@@ -285,6 +293,10 @@
private final int mRoaming;
private final int mDefaultNetwork;
private final int mSubType;
+ /**
+ * The subscriber Id match rule defines how the template should match networks with
+ * specific subscriberId(s). See NetworkTemplate#SUBSCRIBER_ID_MATCH_RULE_* for more detail.
+ */
private final int mSubscriberIdMatchRule;
// Bitfield containing OEM network properties{@code NetworkIdentity#OEM_*}.
@@ -348,7 +360,7 @@
mSubscriberIdMatchRule = subscriberIdMatchRule;
checkValidSubscriberIdMatchRule();
if (!isKnownMatchRule(matchRule)) {
- Log.e(TAG, "Unknown network template rule " + matchRule
+ throw new IllegalArgumentException("Unknown network template rule " + matchRule
+ " will not match any identity.");
}
}
@@ -842,11 +854,17 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
+ if (!isPersistable()) {
+ Log.wtf(TAG, "Trying to backup non-persistable template: " + this);
+ }
+
out.writeInt(BACKUP_VERSION);
out.writeInt(mMatchRule);
BackupUtils.writeString(out, mSubscriberId);
BackupUtils.writeString(out, mNetworkId);
+ out.writeInt(mMetered);
+ out.writeInt(mSubscriberIdMatchRule);
return baos.toByteArray();
}
@@ -854,7 +872,7 @@
public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in)
throws IOException, BackupUtils.BadVersionException {
int version = in.readInt();
- if (version < 1 || version > BACKUP_VERSION) {
+ if (version < BACKUP_VERSION_1_INIT || version > BACKUP_VERSION) {
throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
}
@@ -862,11 +880,27 @@
String subscriberId = BackupUtils.readString(in);
String networkId = BackupUtils.readString(in);
- if (!isKnownMatchRule(matchRule)) {
- throw new BackupUtils.BadVersionException(
- "Restored network template contains unknown match rule " + matchRule);
+ final int metered;
+ final int subscriberIdMatchRule;
+ if (version >= BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE) {
+ metered = in.readInt();
+ subscriberIdMatchRule = in.readInt();
+ } else {
+ // For backward compatibility, fill the missing filters from match rules.
+ metered = (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD
+ || matchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL;
+ subscriberIdMatchRule = SUBSCRIBER_ID_MATCH_RULE_EXACT;
}
- return new NetworkTemplate(matchRule, subscriberId, networkId);
+ try {
+ return new NetworkTemplate(matchRule,
+ subscriberId, new String[] { subscriberId },
+ networkId, metered, NetworkStats.ROAMING_ALL,
+ NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL,
+ NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule);
+ } catch (IllegalArgumentException e) {
+ throw new BackupUtils.BadVersionException(
+ "Restored network template contains unknown match rule " + matchRule, e);
+ }
}
}
diff --git a/core/java/android/net/RssiCurve.java b/core/java/android/net/RssiCurve.java
index 668e966..02cafb5 100644
--- a/core/java/android/net/RssiCurve.java
+++ b/core/java/android/net/RssiCurve.java
@@ -50,8 +50,10 @@
* the system.
*
* @see ScoredNetwork
+ * @deprecated as part of the {@link NetworkScoreManager} deprecation.
* @hide
*/
+@Deprecated
@SystemApi
public class RssiCurve implements Parcelable {
private static final int DEFAULT_ACTIVE_NETWORK_RSSI_BOOST = 25;
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 64b3bf1..a46bdd9a 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -29,8 +29,10 @@
/**
* A network identifier along with a score for the quality of that network.
*
+ * @deprecated as part of the {@link NetworkScoreManager} deprecation.
* @hide
*/
+@Deprecated
@SystemApi
public class ScoredNetwork implements Parcelable {
diff --git a/core/java/android/net/WifiKey.java b/core/java/android/net/WifiKey.java
index bc9d8c5..2e4ea89 100644
--- a/core/java/android/net/WifiKey.java
+++ b/core/java/android/net/WifiKey.java
@@ -29,8 +29,10 @@
* Information identifying a Wi-Fi network.
* @see NetworkKey
*
+ * @deprecated as part of the {@link NetworkScore} deprecation.
* @hide
*/
+@Deprecated
@SystemApi
public class WifiKey implements Parcelable {
diff --git a/core/java/android/net/nsd/INsdManager.aidl b/core/java/android/net/nsd/INsdManager.aidl
index e9e8935..89e9cdb 100644
--- a/core/java/android/net/nsd/INsdManager.aidl
+++ b/core/java/android/net/nsd/INsdManager.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2012, The Android Open Source Project
+ * 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.
@@ -16,16 +16,15 @@
package android.net.nsd;
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.INsdServiceConnector;
import android.os.Messenger;
/**
- * Interface that NsdService implements
+ * Interface that NsdService implements to connect NsdManager clients.
*
* {@hide}
*/
-interface INsdManager
-{
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- Messenger getMessenger();
- void setEnabled(boolean enable);
+interface INsdManager {
+ INsdServiceConnector connect(INsdManagerCallback cb);
}
diff --git a/core/java/android/net/nsd/INsdManagerCallback.aidl b/core/java/android/net/nsd/INsdManagerCallback.aidl
new file mode 100644
index 0000000..1a262ec
--- /dev/null
+++ b/core/java/android/net/nsd/INsdManagerCallback.aidl
@@ -0,0 +1,39 @@
+/**
+ * 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 android.net.nsd;
+
+import android.os.Messenger;
+import android.net.nsd.NsdServiceInfo;
+
+/**
+ * Callbacks from NsdService to NsdManager
+ * @hide
+ */
+oneway interface INsdManagerCallback {
+ void onDiscoverServicesStarted(int listenerKey, in NsdServiceInfo info);
+ void onDiscoverServicesFailed(int listenerKey, int error);
+ void onServiceFound(int listenerKey, in NsdServiceInfo info);
+ void onServiceLost(int listenerKey, in NsdServiceInfo info);
+ void onStopDiscoveryFailed(int listenerKey, int error);
+ void onStopDiscoverySucceeded(int listenerKey);
+ void onRegisterServiceFailed(int listenerKey, int error);
+ void onRegisterServiceSucceeded(int listenerKey, in NsdServiceInfo info);
+ void onUnregisterServiceFailed(int listenerKey, int error);
+ void onUnregisterServiceSucceeded(int listenerKey);
+ void onResolveServiceFailed(int listenerKey, int error);
+ void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info);
+}
diff --git a/core/java/android/net/nsd/INsdServiceConnector.aidl b/core/java/android/net/nsd/INsdServiceConnector.aidl
new file mode 100644
index 0000000..b06ae55
--- /dev/null
+++ b/core/java/android/net/nsd/INsdServiceConnector.aidl
@@ -0,0 +1,35 @@
+/**
+ * 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 android.net.nsd;
+
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.NsdServiceInfo;
+import android.os.Messenger;
+
+/**
+ * Interface that NsdService implements for each NsdManager client.
+ *
+ * {@hide}
+ */
+interface INsdServiceConnector {
+ void registerService(int listenerKey, in NsdServiceInfo serviceInfo);
+ void unregisterService(int listenerKey);
+ void discoverServices(int listenerKey, in NsdServiceInfo serviceInfo);
+ void stopDiscovery(int listenerKey);
+ void resolveService(int listenerKey, in NsdServiceInfo serviceInfo);
+ void startDaemon();
+}
\ No newline at end of file
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index ae8d010..6c597e2 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -31,17 +31,13 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
-import java.util.concurrent.CountDownLatch;
-
/**
* The Network Service Discovery Manager class provides the API to discover services
* on a network. As an example, if device A and device B are connected over a Wi-Fi
@@ -234,6 +230,11 @@
/** @hide */
public static final int NATIVE_DAEMON_EVENT = BASE + 26;
+ /** @hide */
+ public static final int REGISTER_CLIENT = BASE + 27;
+ /** @hide */
+ public static final int UNREGISTER_CLIENT = BASE + 28;
+
/** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001;
@@ -274,7 +275,7 @@
private static final int FIRST_LISTENER_KEY = 1;
- private final INsdManager mService;
+ private final INsdServiceConnector mService;
private final Context mContext;
private int mListenerKey = FIRST_LISTENER_KEY;
@@ -282,9 +283,7 @@
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
private final Object mMapLock = new Object();
- private final AsyncChannel mAsyncChannel = new AsyncChannel();
- private ServiceHandler mHandler;
- private final CountDownLatch mConnected = new CountDownLatch(1);
+ private final ServiceHandler mHandler;
/**
* Create a new Nsd instance. Applications use
@@ -295,18 +294,108 @@
* is a system private class.
*/
public NsdManager(Context context, INsdManager service) {
- mService = service;
mContext = context;
- init();
+
+ HandlerThread t = new HandlerThread("NsdManager");
+ t.start();
+ mHandler = new ServiceHandler(t.getLooper());
+
+ try {
+ mService = service.connect(new NsdCallbackImpl(mHandler));
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to connect to NsdService");
+ }
+
+ // Only proactively start the daemon if the target SDK < S, otherwise the internal service
+ // would automatically start/stop the native daemon as needed.
+ if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
+ try {
+ mService.startDaemon();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to proactively start daemon");
+ // Continue: the daemon can still be started on-demand later
+ }
+ }
}
- /**
- * @hide
- */
- @VisibleForTesting
- public void disconnect() {
- mAsyncChannel.disconnect();
- mHandler.getLooper().quitSafely();
+ private static class NsdCallbackImpl extends INsdManagerCallback.Stub {
+ private final Handler mServHandler;
+
+ NsdCallbackImpl(Handler serviceHandler) {
+ mServHandler = serviceHandler;
+ }
+
+ private void sendInfo(int message, int listenerKey, NsdServiceInfo info) {
+ mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey, info));
+ }
+
+ private void sendError(int message, int listenerKey, int error) {
+ mServHandler.sendMessage(mServHandler.obtainMessage(message, error, listenerKey));
+ }
+
+ private void sendNoArg(int message, int listenerKey) {
+ mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey));
+ }
+
+ @Override
+ public void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
+ sendInfo(DISCOVER_SERVICES_STARTED, listenerKey, info);
+ }
+
+ @Override
+ public void onDiscoverServicesFailed(int listenerKey, int error) {
+ sendError(DISCOVER_SERVICES_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onServiceFound(int listenerKey, NsdServiceInfo info) {
+ sendInfo(SERVICE_FOUND, listenerKey, info);
+ }
+
+ @Override
+ public void onServiceLost(int listenerKey, NsdServiceInfo info) {
+ sendInfo(SERVICE_LOST, listenerKey, info);
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(int listenerKey, int error) {
+ sendError(STOP_DISCOVERY_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onStopDiscoverySucceeded(int listenerKey) {
+ sendNoArg(STOP_DISCOVERY_SUCCEEDED, listenerKey);
+ }
+
+ @Override
+ public void onRegisterServiceFailed(int listenerKey, int error) {
+ sendError(REGISTER_SERVICE_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ sendInfo(REGISTER_SERVICE_SUCCEEDED, listenerKey, info);
+ }
+
+ @Override
+ public void onUnregisterServiceFailed(int listenerKey, int error) {
+ sendError(UNREGISTER_SERVICE_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onUnregisterServiceSucceeded(int listenerKey) {
+ sendNoArg(UNREGISTER_SERVICE_SUCCEEDED, listenerKey);
+ }
+
+ @Override
+ public void onResolveServiceFailed(int listenerKey, int error) {
+ sendError(RESOLVE_SERVICE_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ sendInfo(RESOLVE_SERVICE_SUCCEEDED, listenerKey, info);
+ }
}
/**
@@ -376,19 +465,6 @@
public void handleMessage(Message message) {
final int what = message.what;
final int key = message.arg2;
- switch (what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- return;
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- mConnected.countDown();
- return;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel lost");
- return;
- default:
- break;
- }
final Object listener;
final NsdServiceInfo ns;
synchronized (mMapLock) {
@@ -504,36 +580,6 @@
}
/**
- * Initialize AsyncChannel
- */
- private void init() {
- final Messenger messenger = getMessenger();
- if (messenger == null) {
- fatal("Failed to obtain service Messenger");
- }
- HandlerThread t = new HandlerThread("NsdManager");
- t.start();
- mHandler = new ServiceHandler(t.getLooper());
- mAsyncChannel.connect(mContext, mHandler, messenger);
- try {
- mConnected.await();
- } catch (InterruptedException e) {
- fatal("Interrupted wait at init");
- }
- if (CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
- return;
- }
- // Only proactively start the daemon if the target SDK < S, otherwise the internal service
- // would automatically start/stop the native daemon as needed.
- mAsyncChannel.sendMessage(DAEMON_STARTUP);
- }
-
- private static void fatal(String msg) {
- Log.e(TAG, msg);
- throw new RuntimeException(msg);
- }
-
- /**
* Register a service to be discovered by other services.
*
* <p> The function call immediately returns after sending a request to register service
@@ -556,7 +602,11 @@
checkServiceInfo(serviceInfo);
checkProtocol(protocolType);
int key = putListener(listener, serviceInfo);
- mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo);
+ try {
+ mService.registerService(key, serviceInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
@@ -574,7 +624,11 @@
*/
public void unregisterService(RegistrationListener listener) {
int id = getListenerKey(listener);
- mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id);
+ try {
+ mService.unregisterService(id);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
@@ -613,7 +667,11 @@
s.setServiceType(serviceType);
int key = putListener(listener, s);
- mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s);
+ try {
+ mService.discoverServices(key, s);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
@@ -634,7 +692,11 @@
*/
public void stopServiceDiscovery(DiscoveryListener listener) {
int id = getListenerKey(listener);
- mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id);
+ try {
+ mService.stopDiscovery(id);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
@@ -649,29 +711,10 @@
public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
checkServiceInfo(serviceInfo);
int key = putListener(listener, serviceInfo);
- mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo);
- }
-
- /** Internal use only @hide */
- public void setEnabled(boolean enabled) {
try {
- mService.setEnabled(enabled);
+ mService.resolveService(key, serviceInfo);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get a reference to NsdService handler. This is used to establish
- * an AsyncChannel communication with the service
- *
- * @return Messenger pointing to the NsdService handler
- */
- private Messenger getMessenger() {
- try {
- return mService.getMessenger();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/net/nsd/NsdServiceInfo.aidl
similarity index 77%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to core/java/android/net/nsd/NsdServiceInfo.aidl
index 3729c09..657bdd1 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/java/android/net/nsd/NsdServiceInfo.aidl
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package android.window;
+package android.net.nsd;
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+@JavaOnlyStableParcelable parcelable NsdServiceInfo;
\ No newline at end of file
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 0af322e..0954013 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -528,6 +528,7 @@
public String toString() {
StringBuilder out = new StringBuilder("ApduService: ");
out.append(getComponent());
+ out.append(", UID: " + mUid);
out.append(", description: " + mDescription);
out.append(", Static AID Groups: ");
for (AidGroup aidGroup : mStaticAidGroups.values()) {
@@ -546,7 +547,8 @@
if (!(o instanceof ApduServiceInfo)) return false;
ApduServiceInfo thatService = (ApduServiceInfo) o;
- return thatService.getComponent().equals(this.getComponent());
+ return thatService.getComponent().equals(this.getComponent())
+ && thatService.getUid() == this.getUid();
}
@Override
@@ -619,8 +621,9 @@
};
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" " + getComponent() +
- " (Description: " + getDescription() + ")");
+ pw.println(" " + getComponent()
+ + " (Description: " + getDescription() + ")"
+ + " (UID: " + getUid() + ")");
if (mOnHost) {
pw.println(" On Host Service");
} else {
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index d498535..0a9fe90 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -30,6 +30,7 @@
import android.nfc.INfcCardEmulation;
import android.nfc.NfcAdapter;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
@@ -83,6 +84,13 @@
public static final String EXTRA_SERVICE_COMPONENT = "component";
/**
+ * The caller userId extra for {@link #ACTION_CHANGE_DEFAULT}.
+ *
+ * @see #ACTION_CHANGE_DEFAULT
+ */
+ public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
+
+ /**
* Category used for NFC payment services.
*/
public static final String CATEGORY_PAYMENT = "payment";
@@ -269,8 +277,8 @@
if (CATEGORY_PAYMENT.equals(category)) {
boolean preferForeground = false;
try {
- preferForeground = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
+ preferForeground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NFC_PAYMENT_FOREGROUND, UserHandle.myUserId()) != 0;
} catch (SettingNotFoundException e) {
}
return preferForeground;
@@ -829,6 +837,28 @@
/**
* @hide
*/
+ public boolean setDefaultForNextTap(int userId, ComponentName service) {
+ try {
+ return sService.setDefaultForNextTap(userId, service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setDefaultForNextTap(userId, service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
public List<ApduServiceInfo> getServices(String category) {
try {
return sService.getServices(mContext.getUserId(), category);
@@ -849,6 +879,28 @@
}
/**
+ * @hide
+ */
+ public List<ApduServiceInfo> getServices(String category, int userId) {
+ try {
+ return sService.getServices(userId, category);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return null;
+ }
+ try {
+ return sService.getServices(userId, category);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return null;
+ }
+ }
+ }
+
+ /**
* A valid AID according to ISO/IEC 7816-4:
* <ul>
* <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
index 80e8579..557e41a 100644
--- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
+++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
@@ -25,7 +25,6 @@
import android.nfc.INfcFCardEmulation;
import android.nfc.NfcAdapter;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.HashMap;
diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
index c2b33dd..f8f7dfe 100644
--- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -237,6 +237,7 @@
public String toString() {
StringBuilder out = new StringBuilder("NfcFService: ");
out.append(getComponent());
+ out.append(", UID: " + mUid);
out.append(", description: " + mDescription);
out.append(", System Code: " + mSystemCode);
if (mDynamicSystemCode != null) {
@@ -257,6 +258,7 @@
NfcFServiceInfo thatService = (NfcFServiceInfo) o;
if (!thatService.getComponent().equals(this.getComponent())) return false;
+ if (thatService.getUid() != this.getUid()) return false;
if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false;
if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false;
if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false;
@@ -321,8 +323,9 @@
};
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" " + getComponent() +
- " (Description: " + getDescription() + ")");
+ pw.println(" " + getComponent()
+ + " (Description: " + getDescription() + ")"
+ + " (UID: " + getUid() + ")");
pw.println(" System Code: " + getSystemCode());
pw.println(" NFCID2: " + getNfcid2());
pw.println(" T3tPmm: " + getT3tPmm());
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a4a76a8..53484d2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -7354,9 +7354,11 @@
pw.print(getHistoryTagPoolUid(i));
pw.print(",\"");
String str = getHistoryTagPoolString(i);
- str = str.replace("\\", "\\\\");
- str = str.replace("\"", "\\\"");
- pw.print(str);
+ if (str != null) {
+ str = str.replace("\\", "\\\\");
+ str = str.replace("\"", "\\\"");
+ pw.print(str);
+ }
pw.print("\"");
pw.println();
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 743468a..35b9ccc 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1294,6 +1294,18 @@
public static class Partition {
/** The name identifying the system partition. */
public static final String PARTITION_NAME_SYSTEM = "system";
+ /** @hide */
+ public static final String PARTITION_NAME_BOOTIMAGE = "bootimage";
+ /** @hide */
+ public static final String PARTITION_NAME_ODM = "odm";
+ /** @hide */
+ public static final String PARTITION_NAME_OEM = "oem";
+ /** @hide */
+ public static final String PARTITION_NAME_PRODUCT = "product";
+ /** @hide */
+ public static final String PARTITION_NAME_SYSTEM_EXT = "system_ext";
+ /** @hide */
+ public static final String PARTITION_NAME_VENDOR = "vendor";
private final String mName;
private final String mFingerprint;
@@ -1350,8 +1362,12 @@
ArrayList<Partition> partitions = new ArrayList();
String[] names = new String[] {
- "bootimage", "odm", "product", "system_ext", Partition.PARTITION_NAME_SYSTEM,
- "vendor"
+ Partition.PARTITION_NAME_BOOTIMAGE,
+ Partition.PARTITION_NAME_ODM,
+ Partition.PARTITION_NAME_PRODUCT,
+ Partition.PARTITION_NAME_SYSTEM_EXT,
+ Partition.PARTITION_NAME_SYSTEM,
+ Partition.PARTITION_NAME_VENDOR
};
for (String name : names) {
String fingerprint = SystemProperties.get("ro." + name + ".build.fingerprint");
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index aff55af..5f2c113 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -110,6 +110,20 @@
@TestApi
public abstract long getDuration();
+ /**
+ * Returns true if this effect could represent a touch haptic feedback.
+ *
+ * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
+ * for each vibration, with the correct usage. When a vibration is played with usage UNKNOWN,
+ * then this method will be used to classify the most common use case and make sure they are
+ * covered by the user settings for "Touch feedback".
+ *
+ * @hide
+ */
+ public boolean isHapticFeedbackCandidate() {
+ return false;
+ }
+
/** @hide */
public abstract void validate();
@@ -314,6 +328,12 @@
/** @hide */
@Override
+ public boolean isHapticFeedbackCandidate() {
+ return mEffect.isHapticFeedbackCandidate();
+ }
+
+ /** @hide */
+ @Override
public void validate() {
mEffect.validate();
}
@@ -431,6 +451,17 @@
/** @hide */
@Override
+ public boolean isHapticFeedbackCandidate() {
+ for (int i = 0; i < mEffects.size(); i++) {
+ if (!mEffects.valueAt(i).isHapticFeedbackCandidate()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** @hide */
+ @Override
public void validate() {
Preconditions.checkArgument(mEffects.size() > 0,
"There should be at least one effect set for a combined effect");
@@ -513,6 +544,9 @@
*/
@TestApi
public static final class Sequential extends CombinedVibration {
+ // If a vibration is playing more than 3 effects, it's probably not haptic feedback
+ private static final long MAX_HAPTIC_FEEDBACK_SEQUENCE_SIZE = 3;
+
private final List<CombinedVibration> mEffects;
private final List<Integer> mDelays;
@@ -575,6 +609,21 @@
/** @hide */
@Override
+ public boolean isHapticFeedbackCandidate() {
+ final int effectCount = mEffects.size();
+ if (effectCount > MAX_HAPTIC_FEEDBACK_SEQUENCE_SIZE) {
+ return false;
+ }
+ for (int i = 0; i < effectCount; i++) {
+ if (!mEffects.get(i).isHapticFeedbackCandidate()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** @hide */
+ @Override
public void validate() {
Preconditions.checkArgument(mEffects.size() > 0,
"There should be at least one effect set for a combined effect");
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index b839706..3f42164 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -20,6 +20,7 @@
import android.os.Bundle;
import android.os.IUserRestrictionsListener;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.os.UserManager;
import android.content.pm.UserInfo;
import android.content.IntentSender;
@@ -59,6 +60,8 @@
List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);
List<UserInfo> getProfiles(int userId, boolean enabledOnly);
int[] getProfileIds(int userId, boolean enabledOnly);
+ boolean isUserTypeEnabled(in String userType);
+ boolean canAddMoreUsersOfType(in String userType);
boolean canAddMoreProfilesToUser(in String userType, int userId, boolean allowedToRemoveOne);
boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
UserInfo getProfileParent(int userId);
@@ -91,6 +94,9 @@
boolean markGuestForDeletion(int userId);
UserInfo findCurrentGuestUser();
boolean isQuietModeEnabled(int userId);
+ UserHandle createUserWithAttributes(in String userName, in String userType, int flags,
+ in Bitmap userIcon,
+ in String accountName, in String accountType, in PersistableBundle accountOptions);
void setSeedAccountData(int userId, in String accountName,
in String accountType, in PersistableBundle accountOptions, boolean persist);
String getSeedAccountName(int userId);
@@ -98,6 +104,7 @@
PersistableBundle getSeedAccountOptions(int userId);
void clearSeedAccountData(int userId);
boolean someUserHasSeedAccount(in String accountName, in String accountType);
+ boolean someUserHasAccount(in String accountName, in String accountType);
boolean isProfile(int userId);
boolean isManagedProfile(int userId);
boolean isCloneProfile(int userId);
diff --git a/core/java/android/os/NewUserRequest.java b/core/java/android/os/NewUserRequest.java
index 2ebc01f..b0e1f91 100644
--- a/core/java/android/os/NewUserRequest.java
+++ b/core/java/android/os/NewUserRequest.java
@@ -17,7 +17,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
/**
* Contains necessary information to create user using
@@ -26,6 +30,7 @@
* @hide
*/
@SystemApi
+@SuppressLint("PackageLayering")
public final class NewUserRequest {
@Nullable
private final String mName;
@@ -33,16 +38,24 @@
private final boolean mEphemeral;
@NonNull
private final String mUserType;
+ private final Bitmap mUserIcon;
+ private final String mAccountName;
+ private final String mAccountType;
+ private final PersistableBundle mAccountOptions;
private NewUserRequest(Builder builder) {
mName = builder.mName;
mAdmin = builder.mAdmin;
mEphemeral = builder.mEphemeral;
mUserType = builder.mUserType;
+ mUserIcon = builder.mUserIcon;
+ mAccountName = builder.mAccountName;
+ mAccountType = builder.mAccountType;
+ mAccountOptions = builder.mAccountOptions;
}
/**
- * Gets the user name.
+ * Returns the name of the user.
*/
@Nullable
public String getName() {
@@ -50,7 +63,7 @@
}
/**
- * Is user Ephemenral?
+ * Returns whether the user is ephemeral.
*
* <p> Ephemeral user will be removed after leaving the foreground.
*/
@@ -59,7 +72,7 @@
}
/**
- * Is user Admin?
+ * Returns whether the user is an admin.
*
* <p> Admin user is with administrative privileges and such user can create and
* delete users.
@@ -69,7 +82,17 @@
}
/**
- * Gets user type.
+ * Returns the calculated flags for user creation.
+ */
+ int getFlags() {
+ int flags = 0;
+ if (isAdmin()) flags |= UserInfo.FLAG_ADMIN;
+ if (isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL;
+ return flags;
+ }
+
+ /**
+ * Returns the user type.
*
* <p> Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and
* {@link USER_TYPE_FULL_GUEST}
@@ -79,25 +102,71 @@
return mUserType;
}
+ /**
+ * Returns the user icon.
+ */
+ @Nullable
+ public Bitmap getUserIcon() {
+ return mUserIcon;
+ }
+
+ /**
+ * Returns the account name.
+ */
+ @Nullable
+ public String getAccountName() {
+ return mAccountName;
+ }
+
+ /**
+ * Returns the account type.
+ */
+ @Nullable
+ public String getAccountType() {
+ return mAccountType;
+ }
+
+ /**
+ * Returns the account options.
+ */
+ @SuppressLint("NullableCollection")
+ @Nullable
+ public PersistableBundle getAccountOptions() {
+ return mAccountOptions;
+ }
+
@Override
public String toString() {
- return String.format(
- "NewUserRequest- UserName:%s, userType:%s, IsAdmin:%s, IsEphemeral:%s.", mName,
- mUserType, mAdmin, mEphemeral);
+ return "NewUserRequest{"
+ + "mName='" + mName + '\''
+ + ", mAdmin=" + mAdmin
+ + ", mEphemeral=" + mEphemeral
+ + ", mUserType='" + mUserType + '\''
+ + ", mAccountName='" + mAccountName + '\''
+ + ", mAccountType='" + mAccountType + '\''
+ + ", mAccountOptions=" + mAccountOptions
+ + '}';
}
/**
* Builder for building {@link NewUserRequest}
*/
+ @SuppressLint("PackageLayering")
public static final class Builder {
private String mName;
private boolean mAdmin;
private boolean mEphemeral;
private String mUserType = UserManager.USER_TYPE_FULL_SECONDARY;
+ private Bitmap mUserIcon;
+ private String mAccountName;
+ private String mAccountType;
+ private PersistableBundle mAccountOptions;
/**
* Sets user name.
+ *
+ * @return This object for method chaining.
*/
@NonNull
public Builder setName(@Nullable String name) {
@@ -110,6 +179,8 @@
*
* <p> Admin user is with administrative privileges and such user can create
* and delete users.
+ *
+ * @return This object for method chaining.
*/
@NonNull
public Builder setAdmin() {
@@ -121,6 +192,8 @@
* Sets user as ephemeral.
*
* <p> Ephemeral user will be removed after leaving the foreground.
+ *
+ * @return This object for method chaining.
*/
@NonNull
public Builder setEphemeral() {
@@ -134,6 +207,8 @@
* Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and
* {@link UserManager.USER_TYPE_FULL_GUEST}. Default value is
* {@link UserManager.USER_TYPE_FULL_SECONDARY}.
+ *
+ * @return This object for method chaining.
*/
@NonNull
public Builder setUserType(@NonNull String type) {
@@ -142,6 +217,54 @@
}
/**
+ * Sets user icon.
+ *
+ * @return This object for method chaining.
+ */
+ @NonNull
+ public Builder setUserIcon(@Nullable Bitmap userIcon) {
+ mUserIcon = userIcon;
+ return this;
+ }
+
+ /**
+ * Sets account name that will be used by the setup wizard to initialize the user.
+ *
+ * @see android.accounts.Account
+ * @return This object for method chaining.
+ */
+ @NonNull
+ public Builder setAccountName(@Nullable String accountName) {
+ mAccountName = accountName;
+ return this;
+ }
+
+ /**
+ * Sets account type for the account to be created. This is required if the account name
+ * is not null. This will be used by the setup wizard to initialize the user.
+ *
+ * @see android.accounts.Account
+ * @return This object for method chaining.
+ */
+ @NonNull
+ public Builder setAccountType(@Nullable String accountType) {
+ mAccountType = accountType;
+ return this;
+ }
+
+ /**
+ * Sets account options that can contain account-specific extra information
+ * to be used by setup wizard to initialize the account for the user.
+ *
+ * @return This object for method chaining.
+ */
+ @NonNull
+ public Builder setAccountOptions(@Nullable PersistableBundle accountOptions) {
+ mAccountOptions = accountOptions;
+ return this;
+ }
+
+ /**
* Builds {@link NewUserRequest}
*
* @throws IllegalStateException if builder is configured with incompatible properties and
@@ -165,6 +288,11 @@
&& mUserType != UserManager.USER_TYPE_FULL_GUEST) {
throw new IllegalStateException("Unsupported user type: " + mUserType);
}
+
+ if (TextUtils.isEmpty(mAccountName) != TextUtils.isEmpty(mAccountType)) {
+ throw new IllegalStateException(
+ "Account name and account type should be provided together.");
+ }
}
}
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 92861fb..1e424d1 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -1,18 +1,6 @@
# Haptics
-per-file CombinedVibrationEffect.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file CombinedVibrationEffect.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file ExternalVibration.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file ExternalVibration.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file IExternalVibrationController.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file IExternalVibratorService.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file IVibratorManagerService.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file NullVibrator.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file SystemVibrator.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file SystemVibratorManager.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file VibrationEffect.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file VibrationEffect.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file Vibrator.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file VibratorManager.java = file:/services/core/java/com/android/server/vibrator/OWNERS
+per-file *Vibration* = file:/services/core/java/com/android/server/vibrator/OWNERS
+per-file *Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
# PowerManager
per-file IPowerManager.aidl = michaelwr@google.com, santoscordon@google.com
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 7bdb6b9..afd0ff7 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3650,22 +3650,47 @@
@NonNull
public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl) {
- final int N = readInt();
- if (N == -1) {
+ return readParcelableListInternal(list, cl, /*clazz*/ null);
+ }
+
+ /**
+ * Same as {@link #readParcelableList(List, ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @NonNull
+ public <T> List<T> readParcelableList(@NonNull List<T> list,
+ @Nullable ClassLoader cl, @NonNull Class<T> clazz) {
+ Objects.requireNonNull(list);
+ Objects.requireNonNull(clazz);
+ return readParcelableListInternal(list, cl, clazz);
+ }
+
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @NonNull
+ private <T> List<T> readParcelableListInternal(@NonNull List<T> list,
+ @Nullable ClassLoader cl, @Nullable Class<T> clazz) {
+ final int n = readInt();
+ if (n == -1) {
list.clear();
return list;
}
- final int M = list.size();
+ final int m = list.size();
int i = 0;
- for (; i < M && i < N; i++) {
- list.set(i, (T) readParcelable(cl));
+ for (; i < m && i < n; i++) {
+ list.set(i, (T) readParcelableInternal(cl, clazz));
}
- for (; i<N; i++) {
- list.add((T) readParcelable(cl));
+ for (; i < n; i++) {
+ list.add((T) readParcelableInternal(cl, clazz));
}
- for (; i<M; i++) {
- list.remove(N);
+ for (; i < m; i++) {
+ list.remove(n);
}
return list;
}
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index a828268..d0d6cb7 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -196,9 +196,6 @@
return;
}
CombinedVibration combinedEffect = CombinedVibration.createParallel(effect);
- // TODO(b/185351540): move this into VibratorManagerService once the touch vibration
- // heuristics is fixed and works for CombinedVibration. Make sure it's always applied.
- attributes = new VibrationAttributes.Builder(attributes, effect).build();
mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes);
}
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index e5622a3..c690df2 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -223,9 +223,6 @@
CombinedVibration combined = CombinedVibration.startParallel()
.addVibrator(mVibratorInfo.getId(), vibe)
.combine();
- // TODO(b/185351540): move this into VibratorManagerService once the touch vibration
- // heuristics is fixed and works for CombinedVibration. Make sure it's always applied.
- attributes = new VibrationAttributes.Builder(attributes, vibe).build();
SystemVibratorManager.this.vibrate(uid, opPkg, combined, reason, attributes);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 94375c0..bc6dbd8 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -70,6 +70,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -307,6 +308,25 @@
public static final String DISALLOW_WIFI_TETHERING = "no_wifi_tethering";
/**
+ * Specifies if users are disallowed from sharing Wi-Fi for admin configured networks.
+ *
+ * <p>Device owner and profile owner can set this restriction.
+ * When it is set by any of these owners, it prevents all users from
+ * sharing Wi-Fi for networks configured by these owners.
+ * Other networks not configured by these owners are not affected.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI =
+ "no_sharing_admin_configured_wifi";
+
+ /**
* Specifies if a user is disallowed from changing the device
* language. The default value is <code>false</code>.
*
@@ -1477,6 +1497,9 @@
DISALLOW_CAMERA_TOGGLE,
KEY_RESTRICTIONS_PENDING,
DISALLOW_BIOMETRIC,
+ DISALLOW_CHANGE_WIFI_STATE,
+ DISALLOW_WIFI_TETHERING,
+ DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserRestrictionKey {}
@@ -1661,6 +1684,14 @@
public static final int USER_OPERATION_ERROR_MAX_USERS = 6;
/**
+ * Indicates user operation failed because a user with that account already exists.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7;
+
+ /**
* Result returned from various user operations.
*
* @hide
@@ -1673,7 +1704,8 @@
USER_OPERATION_ERROR_MAX_RUNNING_USERS,
USER_OPERATION_ERROR_CURRENT_USER,
USER_OPERATION_ERROR_LOW_STORAGE,
- USER_OPERATION_ERROR_MAX_USERS
+ USER_OPERATION_ERROR_MAX_USERS,
+ USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS
})
public @interface UserOperationResult {}
@@ -3159,26 +3191,24 @@
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
public @NonNull NewUserResponse createUser(@NonNull NewUserRequest newUserRequest) {
- UserInfo user = null;
- int operationResult = USER_OPERATION_ERROR_UNKNOWN;
try {
- user = createUser(newUserRequest.getName(), newUserRequest.getUserType(),
- determineFlagsForUserCreation(newUserRequest));
- } catch (UserOperationException e) {
- Log.w(TAG, "Exception while creating user " + newUserRequest, e);
- operationResult = e.getUserOperationResult();
- }
- if (user == null) {
- return new NewUserResponse(null, operationResult);
- }
- return new NewUserResponse(user.getUserHandle(), USER_OPERATION_SUCCESS);
- }
+ final UserHandle userHandle = mService.createUserWithAttributes(
+ newUserRequest.getName(),
+ newUserRequest.getUserType(),
+ newUserRequest.getFlags(),
+ newUserRequest.getUserIcon(),
+ newUserRequest.getAccountName(),
+ newUserRequest.getAccountType(),
+ newUserRequest.getAccountOptions());
- private int determineFlagsForUserCreation(NewUserRequest newUserRequest) {
- int flags = 0;
- if (newUserRequest.isAdmin()) flags |= UserInfo.FLAG_ADMIN;
- if (newUserRequest.isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL;
- return flags;
+ return new NewUserResponse(userHandle, USER_OPERATION_SUCCESS);
+
+ } catch (ServiceSpecificException e) {
+ Log.w(TAG, "Exception while creating user " + newUserRequest, e);
+ return new NewUserResponse(null, e.errorCode);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
@@ -3846,13 +3876,19 @@
}
/**
- * Checks whether it's possible to add more users. Caller must hold the MANAGE_USERS
- * permission.
+ * Checks whether it's possible to add more users.
*
* @return true if more users can be added, false if limit has been reached.
+ *
+ * @deprecated use {@link #canAddMoreUsers(String)} instead.
+ *
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @Deprecated
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS
+ })
public boolean canAddMoreUsers() {
// TODO(b/142482943): UMS has different logic, excluding Demo and Profile from counting. Why
// not here? The logic is inconsistent. See UMS.canAddMoreManagedProfiles
@@ -3869,6 +3905,25 @@
}
/**
+ * Checks whether it is possible to add more users of the given user type.
+ *
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_SECONDARY}.
+ * @return true if more users of the given type can be added, otherwise false.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS
+ })
+ public boolean canAddMoreUsers(@NonNull String userType) {
+ try {
+ return canAddMoreUsers() && mService.canAddMoreUsersOfType(userType);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Checks whether it's possible to add more managed profiles. Caller must hold the MANAGE_USERS
* permission.
* if allowedToRemoveOne is true and if the user already has a managed profile, then return if
@@ -3903,6 +3958,25 @@
}
/**
+ * Checks whether this device supports users of the given user type.
+ *
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_SECONDARY}.
+ * @return true if the creation of users of the given user type is enabled on this device.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS
+ })
+ public boolean isUserTypeEnabled(@NonNull String userType) {
+ try {
+ return mService.isUserTypeEnabled(userType);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns list of the profiles of userId including userId itself.
* Note that this returns both enabled and not enabled profiles. See
* {@link #getEnabledProfiles(int)} if you need only the enabled ones.
@@ -4913,12 +4987,12 @@
}
/**
- * @hide
* Checks if any uninitialized user has the specific seed account name and type.
*
* @param accountName The account name to check for
* @param accountType The account type of the account to check for
* @return whether the seed account was found
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean someUserHasSeedAccount(String accountName, String accountType) {
@@ -4930,6 +5004,29 @@
}
/**
+ * Checks if any initialized or uninitialized user has the specific account name and type.
+ *
+ * @param accountName The account name to check for
+ * @param accountType The account type of the account to check for
+ * @return whether the account was found
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ public boolean someUserHasAccount(
+ @NonNull String accountName, @NonNull String accountType) {
+ Objects.requireNonNull(accountName, "accountName must not be null");
+ Objects.requireNonNull(accountType, "accountType must not be null");
+
+ try {
+ return mService.someUserHasAccount(accountName, accountType);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @hide
* User that enforces a restriction.
*
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index e986036..9612ca6 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -21,9 +21,6 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.media.AudioAttributes;
-import android.os.vibrator.PrebakedSegment;
-import android.os.vibrator.VibrationEffectSegment;
-import android.util.Slog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -81,6 +78,11 @@
* actions, such as emulation of physical effects, and texting feedback vibration.
*/
public static final int USAGE_CLASS_FEEDBACK = 0x2;
+ /**
+ * Vibration usage class value to use when the vibration is part of media, such as music, movie,
+ * soundtrack, game or animations.
+ */
+ public static final int USAGE_CLASS_MEDIA = 0x3;
/**
* Mask for vibration usage class value.
@@ -121,6 +123,15 @@
* such as a fingerprint sensor.
*/
public static final int USAGE_HARDWARE_FEEDBACK = 0x30 | USAGE_CLASS_FEEDBACK;
+ /**
+ * Usage value to use for accessibility vibrations, such as with a screen reader.
+ */
+ public static final int USAGE_ACCESSIBILITY = 0x40 | USAGE_CLASS_FEEDBACK;
+ /**
+ * Usage value to use for media vibrations, such as music, movie, soundtrack, animations, games,
+ * or any interactive media that isn't for touch feedback specifically.
+ */
+ public static final int USAGE_MEDIA = 0x10 | USAGE_CLASS_MEDIA;
/**
* @hide
@@ -142,9 +153,6 @@
*/
public static final int FLAG_ALL_SUPPORTED = FLAG_BYPASS_INTERRUPTION_POLICY;
- // If a vibration is playing for longer than 5s, it's probably not haptic feedback
- private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
-
/** Creates a new {@link VibrationAttributes} instance with given usage. */
public static @NonNull VibrationAttributes createForUsage(int usage) {
return new VibrationAttributes.Builder().setUsage(usage).build();
@@ -208,13 +216,17 @@
case USAGE_NOTIFICATION:
return AudioAttributes.USAGE_NOTIFICATION;
case USAGE_COMMUNICATION_REQUEST:
- return AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST;
+ return AudioAttributes.USAGE_VOICE_COMMUNICATION;
case USAGE_RINGTONE:
return AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
case USAGE_TOUCH:
return AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
case USAGE_ALARM:
return AudioAttributes.USAGE_ALARM;
+ case USAGE_ACCESSIBILITY:
+ return AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
+ case USAGE_MEDIA:
+ return AudioAttributes.USAGE_MEDIA;
default:
return AudioAttributes.USAGE_UNKNOWN;
}
@@ -286,12 +298,16 @@
return "UNKNOWN";
case USAGE_ALARM:
return "ALARM";
+ case USAGE_ACCESSIBILITY:
+ return "ACCESSIBILITY";
case USAGE_RINGTONE:
return "RINGTONE";
case USAGE_NOTIFICATION:
return "NOTIFICATION";
case USAGE_COMMUNICATION_REQUEST:
return "COMMUNICATION_REQUEST";
+ case USAGE_MEDIA:
+ return "MEDIA";
case USAGE_TOUCH:
return "TOUCH";
case USAGE_PHYSICAL_EMULATION:
@@ -337,67 +353,6 @@
setFlags(audio);
}
- /**
- * Constructs a new Builder from AudioAttributes and a VibrationEffect to infer usage.
- * @hide
- */
- @TestApi
- public Builder(@NonNull AudioAttributes audio, @NonNull VibrationEffect effect) {
- this(audio);
- applyHapticFeedbackHeuristics(effect);
- }
-
- /**
- * Constructs a new Builder from VibrationAttributes and a VibrationEffect to infer usage.
- * @hide
- */
- @TestApi
- public Builder(@NonNull VibrationAttributes vib, @NonNull VibrationEffect effect) {
- this(vib);
- applyHapticFeedbackHeuristics(effect);
- }
-
- private void applyHapticFeedbackHeuristics(@Nullable VibrationEffect effect) {
- if (effect != null) {
- PrebakedSegment prebaked = extractPrebakedSegment(effect);
- if (mUsage == USAGE_UNKNOWN && prebaked != null) {
- switch (prebaked.getEffectId()) {
- case VibrationEffect.EFFECT_CLICK:
- case VibrationEffect.EFFECT_DOUBLE_CLICK:
- case VibrationEffect.EFFECT_HEAVY_CLICK:
- case VibrationEffect.EFFECT_TEXTURE_TICK:
- case VibrationEffect.EFFECT_TICK:
- case VibrationEffect.EFFECT_POP:
- case VibrationEffect.EFFECT_THUD:
- mUsage = USAGE_TOUCH;
- break;
- default:
- Slog.w(TAG, "Unknown prebaked vibration effect, assuming it isn't "
- + "haptic feedback");
- }
- }
- final long duration = effect.getDuration();
- if (mUsage == USAGE_UNKNOWN && duration >= 0
- && duration < MAX_HAPTIC_FEEDBACK_DURATION) {
- mUsage = USAGE_TOUCH;
- }
- }
- }
-
- @Nullable
- private PrebakedSegment extractPrebakedSegment(VibrationEffect effect) {
- if (effect instanceof VibrationEffect.Composed) {
- VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
- if (composed.getSegments().size() == 1) {
- VibrationEffectSegment segment = composed.getSegments().get(0);
- if (segment instanceof PrebakedSegment) {
- return (PrebakedSegment) segment;
- }
- }
- }
- return null;
- }
-
private void setUsage(@NonNull AudioAttributes audio) {
mOriginalAudioUsage = audio.getUsage();
switch (audio.getUsage()) {
@@ -405,21 +360,31 @@
case AudioAttributes.USAGE_NOTIFICATION_EVENT:
case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
mUsage = USAGE_NOTIFICATION;
break;
- case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
- case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
+ case AudioAttributes.USAGE_VOICE_COMMUNICATION:
+ case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING:
+ case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+ case AudioAttributes.USAGE_ASSISTANT:
mUsage = USAGE_COMMUNICATION_REQUEST;
break;
case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
mUsage = USAGE_RINGTONE;
break;
+ case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
+ mUsage = USAGE_ACCESSIBILITY;
+ break;
case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
mUsage = USAGE_TOUCH;
break;
case AudioAttributes.USAGE_ALARM:
mUsage = USAGE_ALARM;
break;
+ case AudioAttributes.USAGE_MEDIA:
+ case AudioAttributes.USAGE_GAME:
+ mUsage = USAGE_MEDIA;
+ break;
default:
mUsage = USAGE_UNKNOWN;
}
@@ -450,6 +415,8 @@
* {@link VibrationAttributes#USAGE_TOUCH},
* {@link VibrationAttributes#USAGE_PHYSICAL_EMULATION},
* {@link VibrationAttributes#USAGE_HARDWARE_FEEDBACK}.
+ * {@link VibrationAttributes#USAGE_ACCESSIBILITY}.
+ * {@link VibrationAttributes#USAGE_MEDIA}.
* @return the same Builder instance.
*/
public @NonNull Builder setUsage(int usage) {
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index a0cbbfe..5758a4e 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -53,7 +53,10 @@
public abstract class VibrationEffect implements Parcelable {
// Stevens' coefficient to scale the perceived vibration intensity.
private static final float SCALE_GAMMA = 0.65f;
-
+ // If a vibration is playing for longer than 1s, it's probably not haptic feedback
+ private static final long MAX_HAPTIC_FEEDBACK_DURATION = 1000;
+ // If a vibration is playing more than 3 constants, it's probably not haptic feedback
+ private static final long MAX_HAPTIC_FEEDBACK_COMPOSITION_SIZE = 3;
/**
* The default vibration strength of the device.
@@ -439,6 +442,20 @@
public abstract long getDuration();
/**
+ * Returns true if this effect could represent a touch haptic feedback.
+ *
+ * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
+ * for each vibration, with the correct usage. When a vibration is played with usage UNKNOWN,
+ * then this method will be used to classify the most common use case and make sure they are
+ * covered by the user settings for "Touch feedback".
+ *
+ * @hide
+ */
+ public boolean isHapticFeedbackCandidate() {
+ return false;
+ }
+
+ /**
* Resolve default values into integer amplitude numbers.
*
* @param defaultAmplitude the default amplitude to apply, must be between 0 and
@@ -582,6 +599,7 @@
return mRepeatIndex;
}
+ /** @hide */
@Override
public void validate() {
int segmentCount = mSegments.size();
@@ -620,6 +638,37 @@
return totalDuration;
}
+ /** @hide */
+ @Override
+ public boolean isHapticFeedbackCandidate() {
+ long totalDuration = getDuration();
+ if (totalDuration > MAX_HAPTIC_FEEDBACK_DURATION) {
+ // Vibration duration is known and is longer than the max duration used to classify
+ // haptic feedbacks (or repeating indefinitely with duration == Long.MAX_VALUE).
+ return false;
+ }
+ int segmentCount = mSegments.size();
+ if (segmentCount > MAX_HAPTIC_FEEDBACK_COMPOSITION_SIZE) {
+ // Vibration has some prebaked or primitive constants, it should be limited to the
+ // max composition size used to classify haptic feedbacks.
+ return false;
+ }
+ totalDuration = 0;
+ for (int i = 0; i < segmentCount; i++) {
+ if (!mSegments.get(i).isHapticFeedbackCandidate()) {
+ // There is at least one segment that is not a candidate for a haptic feedback.
+ return false;
+ }
+ long segmentDuration = mSegments.get(i).getDuration();
+ if (segmentDuration > 0) {
+ totalDuration += segmentDuration;
+ }
+ }
+ // Vibration might still have some ramp or step segments, check the known duration.
+ return totalDuration <= MAX_HAPTIC_FEEDBACK_DURATION;
+ }
+
+ /** @hide */
@NonNull
@Override
public Composed resolve(int defaultAmplitude) {
@@ -636,6 +685,7 @@
return resolved;
}
+ /** @hide */
@NonNull
@Override
public Composed scale(float scaleFactor) {
@@ -652,6 +702,7 @@
return scaled;
}
+ /** @hide */
@NonNull
@Override
public Composed applyEffectStrength(int effectStrength) {
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 75234db..c67c82e 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -493,7 +493,7 @@
vibrate(vibe,
attributes == null
? new VibrationAttributes.Builder().build()
- : new VibrationAttributes.Builder(attributes, vibe).build());
+ : new VibrationAttributes.Builder(attributes).build());
}
/**
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index f57d157..39a2e13 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -56,11 +56,12 @@
* <ul>
* <li>{@link #MOUNT_FLAG_PRIMARY} means the volume provides primary external
* storage, historically found at {@code /sdcard}.
- * <li>{@link #MOUNT_FLAG_VISIBLE} means the volume is visible to third-party
- * apps for direct filesystem access. The system should send out relevant
- * storage broadcasts and index any media on visible volumes. Visible volumes
- * are considered a more stable part of the device, which is why we take the
- * time to index them. In particular, transient volumes like USB OTG devices
+ * <li>{@link #MOUNT_FLAG_VISIBLE_FOR_READ} and
+ * {@link #MOUNT_FLAG_VISIBLE_FOR_WRITE} mean the volume is visible to
+ * third-party apps for direct filesystem access. The system should send out
+ * relevant storage broadcasts and index any media on visible volumes. Visible
+ * volumes are considered a more stable part of the device, which is why we take
+ * the time to index them. In particular, transient volumes like USB OTG devices
* <em>should not</em> be marked as visible; their contents should be surfaced
* to apps through the Storage Access Framework.
* </ul>
@@ -100,7 +101,8 @@
public static final int STATE_BAD_REMOVAL = IVold.VOLUME_STATE_BAD_REMOVAL;
public static final int MOUNT_FLAG_PRIMARY = IVold.MOUNT_FLAG_PRIMARY;
- public static final int MOUNT_FLAG_VISIBLE = IVold.MOUNT_FLAG_VISIBLE;
+ public static final int MOUNT_FLAG_VISIBLE_FOR_READ = IVold.MOUNT_FLAG_VISIBLE_FOR_READ;
+ public static final int MOUNT_FLAG_VISIBLE_FOR_WRITE = IVold.MOUNT_FLAG_VISIBLE_FOR_WRITE;
private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
@@ -312,17 +314,31 @@
return isPrimary() && (getType() == TYPE_PUBLIC);
}
- @UnsupportedAppUsage
- public boolean isVisible() {
- return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
+ private boolean isVisibleForRead() {
+ return (mountFlags & MOUNT_FLAG_VISIBLE_FOR_READ) != 0;
}
- public boolean isVisibleForUser(int userId) {
- if ((type == TYPE_PUBLIC || type == TYPE_STUB || type == TYPE_EMULATED)
- && mountUserId == userId) {
- return isVisible();
+ private boolean isVisibleForWrite() {
+ return (mountFlags & MOUNT_FLAG_VISIBLE_FOR_WRITE) != 0;
+ }
+
+ @UnsupportedAppUsage
+ public boolean isVisible() {
+ return isVisibleForRead() || isVisibleForWrite();
+ }
+
+ private boolean isVolumeSupportedForUser(int userId) {
+ if (mountUserId != userId) {
+ return false;
}
- return false;
+ return type == TYPE_PUBLIC || type == TYPE_STUB || type == TYPE_EMULATED;
+ }
+
+ /**
+ * Returns {@code true} if this volume is visible for {@code userId}, {@code false} otherwise.
+ */
+ public boolean isVisibleForUser(int userId) {
+ return isVolumeSupportedForUser(userId) && isVisible();
}
/**
@@ -335,12 +351,12 @@
}
public boolean isVisibleForRead(int userId) {
- return isVisibleForUser(userId);
+ return isVolumeSupportedForUser(userId) && isVisibleForRead();
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isVisibleForWrite(int userId) {
- return isVisibleForUser(userId);
+ return isVolumeSupportedForUser(userId) && isVisibleForWrite();
}
@UnsupportedAppUsage
diff --git a/core/java/android/os/vibrator/OWNERS b/core/java/android/os/vibrator/OWNERS
new file mode 100644
index 0000000..b54d6bf
--- /dev/null
+++ b/core/java/android/os/vibrator/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 78b4346..30f5a5c 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -67,17 +67,38 @@
return -1;
}
+ /** @hide */
+ @Override
+ public boolean isHapticFeedbackCandidate() {
+ switch (mEffectId) {
+ case VibrationEffect.EFFECT_CLICK:
+ case VibrationEffect.EFFECT_DOUBLE_CLICK:
+ case VibrationEffect.EFFECT_HEAVY_CLICK:
+ case VibrationEffect.EFFECT_POP:
+ case VibrationEffect.EFFECT_TEXTURE_TICK:
+ case VibrationEffect.EFFECT_THUD:
+ case VibrationEffect.EFFECT_TICK:
+ return true;
+ default:
+ // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
+ return false;
+ }
+ }
+
+ /** @hide */
@Override
public boolean hasNonZeroAmplitude() {
return true;
}
+ /** @hide */
@NonNull
@Override
public PrebakedSegment resolve(int defaultAmplitude) {
return this;
}
+ /** @hide */
@NonNull
@Override
public PrebakedSegment scale(float scaleFactor) {
@@ -85,6 +106,7 @@
return this;
}
+ /** @hide */
@NonNull
@Override
public PrebakedSegment applyEffectStrength(int effectStrength) {
@@ -105,16 +127,17 @@
}
}
+ /** @hide */
@Override
public void validate() {
switch (mEffectId) {
case VibrationEffect.EFFECT_CLICK:
case VibrationEffect.EFFECT_DOUBLE_CLICK:
- case VibrationEffect.EFFECT_TICK:
+ case VibrationEffect.EFFECT_HEAVY_CLICK:
+ case VibrationEffect.EFFECT_POP:
case VibrationEffect.EFFECT_TEXTURE_TICK:
case VibrationEffect.EFFECT_THUD:
- case VibrationEffect.EFFECT_POP:
- case VibrationEffect.EFFECT_HEAVY_CLICK:
+ case VibrationEffect.EFFECT_TICK:
break;
default:
int[] ringtones = VibrationEffect.RINGTONES;
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 2ef29cb..58ca978 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -67,18 +67,27 @@
return -1;
}
+ /** @hide */
+ @Override
+ public boolean isHapticFeedbackCandidate() {
+ return true;
+ }
+
+ /** @hide */
@Override
public boolean hasNonZeroAmplitude() {
// Every primitive plays a vibration with a non-zero amplitude, even at scale == 0.
return true;
}
+ /** @hide */
@NonNull
@Override
public PrimitiveSegment resolve(int defaultAmplitude) {
return this;
}
+ /** @hide */
@NonNull
@Override
public PrimitiveSegment scale(float scaleFactor) {
@@ -86,12 +95,14 @@
mDelay);
}
+ /** @hide */
@NonNull
@Override
public PrimitiveSegment applyEffectStrength(int effectStrength) {
return this;
}
+ /** @hide */
@Override
public void validate() {
Preconditions.checkArgumentInRange(mPrimitiveId, VibrationEffect.Composition.PRIMITIVE_NOOP,
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index aad87c5..3ec5636 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -87,11 +87,19 @@
return mDuration;
}
+ /** @hide */
+ @Override
+ public boolean isHapticFeedbackCandidate() {
+ return true;
+ }
+
+ /** @hide */
@Override
public boolean hasNonZeroAmplitude() {
return mStartAmplitude > 0 || mEndAmplitude > 0;
}
+ /** @hide */
@Override
public void validate() {
Preconditions.checkArgumentNonnegative(mDuration,
@@ -100,7 +108,7 @@
Preconditions.checkArgumentInRange(mEndAmplitude, 0f, 1f, "endAmplitude");
}
-
+ /** @hide */
@NonNull
@Override
public RampSegment resolve(int defaultAmplitude) {
@@ -108,6 +116,7 @@
return this;
}
+ /** @hide */
@NonNull
@Override
public RampSegment scale(float scaleFactor) {
@@ -121,6 +130,7 @@
mDuration);
}
+ /** @hide */
@NonNull
@Override
public RampSegment applyEffectStrength(int effectStrength) {
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 11209e0..69a381f 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -73,12 +73,20 @@
return mDuration;
}
+ /** @hide */
+ @Override
+ public boolean isHapticFeedbackCandidate() {
+ return true;
+ }
+
+ /** @hide */
@Override
public boolean hasNonZeroAmplitude() {
// DEFAULT_AMPLITUDE == -1 is still a non-zero amplitude that will be resolved later.
return Float.compare(mAmplitude, 0) != 0;
}
+ /** @hide */
@Override
public void validate() {
Preconditions.checkArgumentNonnegative(mDuration,
@@ -88,6 +96,7 @@
}
}
+ /** @hide */
@NonNull
@Override
public StepSegment resolve(int defaultAmplitude) {
@@ -103,6 +112,7 @@
mDuration);
}
+ /** @hide */
@NonNull
@Override
public StepSegment scale(float scaleFactor) {
@@ -113,6 +123,7 @@
mDuration);
}
+ /** @hide */
@NonNull
@Override
public StepSegment applyEffectStrength(int effectStrength) {
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 5b42845..979c447 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -57,10 +57,26 @@
*/
public abstract long getDuration();
- /** Returns true if this segment plays at a non-zero amplitude at some point. */
+ /**
+ * Returns true if this segment could be a haptic feedback effect candidate.
+ *
+ * @see VibrationEffect#isHapticFeedbackCandidate()
+ * @hide
+ */
+ public abstract boolean isHapticFeedbackCandidate();
+
+ /**
+ * Returns true if this segment plays at a non-zero amplitude at some point.
+ *
+ * @hide
+ */
public abstract boolean hasNonZeroAmplitude();
- /** Validates the segment, throwing exceptions if any parameter is invalid. */
+ /**
+ * Validates the segment, throwing exceptions if any parameter is invalid.
+ *
+ * @hide
+ */
public abstract void validate();
/**
@@ -68,6 +84,8 @@
*
* <p>This might fail with {@link IllegalArgumentException} if value is non-positive or larger
* than {@link VibrationEffect#MAX_AMPLITUDE}.
+ *
+ * @hide
*/
@NonNull
public abstract <T extends VibrationEffectSegment> T resolve(int defaultAmplitude);
@@ -77,6 +95,8 @@
*
* @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
* scale down the intensity, values larger than 1 will scale up
+ *
+ * @hide
*/
@NonNull
public abstract <T extends VibrationEffectSegment> T scale(float scaleFactor);
@@ -86,6 +106,8 @@
*
* @param effectStrength new effect strength to be applied, one of
* VibrationEffect.EFFECT_STRENGTH_*.
+ *
+ * @hide
*/
@NonNull
public abstract <T extends VibrationEffectSegment> T applyEffectStrength(int effectStrength);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ae09b45..7979256 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -54,6 +54,7 @@
import android.database.SQLException;
import android.location.ILocationManager;
import android.location.LocationManager;
+import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.net.Uri;
@@ -269,6 +270,16 @@
public static final String EXTRA_NETWORK_TEMPLATE = "network_template";
/**
+ * Activity Action: Show One-handed mode Settings page.
+ * <p>
+ * Input: Nothing
+ * <p>
+ * Output: Nothing
+ * @hide
+ */
+ public static final String ACTION_ONE_HANDED_SETTINGS =
+ "android.settings.action.ONE_HANDED_SETTINGS";
+ /**
* The return values for {@link Settings.Config#set}
* @hide
*/
@@ -3622,6 +3633,12 @@
private static boolean putStringForUser(ContentResolver resolver, String name, String value,
int userHandle, boolean overrideableByRestore) {
+ return putStringForUser(resolver, name, value, /* tag= */ null,
+ /* makeDefault= */ false, userHandle, overrideableByRestore);
+ }
+
+ private static boolean putStringForUser(ContentResolver resolver, String name, String value,
+ String tag, boolean makeDefault, int userHandle, boolean overrideableByRestore) {
if (MOVED_TO_SECURE.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Secure, value is unchanged.");
@@ -3632,8 +3649,8 @@
+ " to android.provider.Settings.Global, value is unchanged.");
return false;
}
- return sNameValueCache.putStringForUser(resolver, name, value, null, false, userHandle,
- overrideableByRestore);
+ return sNameValueCache.putStringForUser(resolver, name, value, tag, makeDefault,
+ userHandle, overrideableByRestore);
}
/**
@@ -4469,6 +4486,15 @@
public static final String VIBRATE_ON = "vibrate_on";
/**
+ * Whether applying ramping ringer on incoming phone call ringtone.
+ * <p>1 = apply ramping ringer
+ * <p>0 = do not apply ramping ringer
+ * @hide
+ */
+ @Readable
+ public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
+
+ /**
* If 1, redirects the system vibrator to all currently attached input devices
* that support vibration. If there are no such input devices, then the system
* vibrator is used instead.
@@ -4537,6 +4563,25 @@
"haptic_feedback_intensity";
/**
+ * The intensity of haptic feedback vibrations for interaction with hardware components from
+ * the device, like buttons and sensors, if configurable.
+ *
+ * Not all devices are capable of changing their feedback intensity; on these devices
+ * there will likely be no difference between the various vibration intensities except for
+ * intensity 0 (off) and the rest.
+ *
+ * <b>Values:</b><br/>
+ * 0 - Vibration is disabled<br/>
+ * 1 - Weak vibrations<br/>
+ * 2 - Medium vibrations<br/>
+ * 3 - Strong vibrations
+ * @hide
+ */
+ @Readable
+ public static final String HARDWARE_HAPTIC_FEEDBACK_INTENSITY =
+ "hardware_haptic_feedback_intensity";
+
+ /**
* Ringer volume. This is used internally, changing this value will not
* change the volume. See AudioManager.
*
@@ -5319,6 +5364,7 @@
PUBLIC_SETTINGS.add(HAPTIC_FEEDBACK_ENABLED);
PUBLIC_SETTINGS.add(SHOW_WEB_SUGGESTIONS);
PUBLIC_SETTINGS.add(VIBRATE_WHEN_RINGING);
+ PUBLIC_SETTINGS.add(APPLY_RAMPING_RINGER);
}
/**
@@ -5856,6 +5902,10 @@
}
/** @hide */
+ public static void getMovedToSystemSettings(Set<String> outKeySet) {
+ }
+
+ /** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
sNameValueCache.clearGenerationTrackerForTest();
@@ -10453,7 +10503,9 @@
* Whether applying ramping ringer on incoming phone call ringtone.
* <p>1 = apply ramping ringer
* <p>0 = do not apply ramping ringer
+ * @deprecated Use {@link AudioManager#isRampingRingerEnabled()} instead
*/
+ @Deprecated
@Readable
public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
@@ -11962,8 +12014,10 @@
* Value to specify whether network quality scores and badging should be shown in the UI.
*
* Type: int (0 for false, 1 for true)
+ * @deprecated {@code NetworkScoreManager} is deprecated.
* @hide
*/
+ @Deprecated
@Readable
public static final String NETWORK_SCORING_UI_ENABLED = "network_scoring_ui_enabled";
@@ -11972,8 +12026,10 @@
* when generating SSID only bases score curves.
*
* Type: long
+ * @deprecated {@code NetworkScoreManager} is deprecated.
* @hide
*/
+ @Deprecated
@Readable
public static final String SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS =
"speed_label_cache_eviction_age_millis";
@@ -12006,8 +12062,10 @@
* {@link NetworkScoreManager#setActiveScorer(String)} to write it.
*
* Type: string - package name
+ * @deprecated {@code NetworkScoreManager} is deprecated.
* @hide
*/
+ @Deprecated
@Readable
public static final String NETWORK_RECOMMENDATIONS_PACKAGE =
"network_recommendations_package";
@@ -12017,8 +12075,10 @@
* networks automatically.
*
* Type: string package name or null if the feature is either not provided or disabled.
+ * @deprecated {@code NetworkScoreManager} is deprecated.
* @hide
*/
+ @Deprecated
@TestApi
@Readable
public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
@@ -12028,8 +12088,10 @@
* {@link com.android.server.wifi.RecommendedNetworkEvaluator}.
*
* Type: long
+ * @deprecated {@code NetworkScoreManager} is deprecated.
* @hide
*/
+ @Deprecated
@Readable
public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS =
"recommended_network_evaluator_cache_expiry_ms";
@@ -15232,12 +15294,24 @@
MOVED_TO_SECURE.add(Global.NOTIFICATION_BUBBLES);
}
+ // Certain settings have been moved from global to the per-user system namespace
+ private static final HashSet<String> MOVED_TO_SYSTEM;
+ static {
+ MOVED_TO_SYSTEM = new HashSet<>(1);
+ MOVED_TO_SYSTEM.add(Global.APPLY_RAMPING_RINGER);
+ }
+
/** @hide */
public static void getMovedToSecureSettings(Set<String> outKeySet) {
outKeySet.addAll(MOVED_TO_SECURE);
}
/** @hide */
+ public static void getMovedToSystemSettings(Set<String> outKeySet) {
+ outKeySet.addAll(MOVED_TO_SYSTEM);
+ }
+
+ /** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
sNameValueCache.clearGenerationTrackerForTest();
@@ -15269,6 +15343,11 @@
+ " to android.provider.Settings.Secure, returning read-only value.");
return Secure.getStringForUser(resolver, name, userHandle);
}
+ if (MOVED_TO_SYSTEM.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global"
+ + " to android.provider.Settings.System, returning read-only value.");
+ return System.getStringForUser(resolver, name, userHandle);
+ }
return sNameValueCache.getStringForUser(resolver, name, userHandle);
}
@@ -15433,6 +15512,13 @@
return Secure.putStringForUser(resolver, name, value, tag,
makeDefault, userHandle, overrideableByRestore);
}
+ // Global and System have the same access policy so we can forward writes
+ if (MOVED_TO_SYSTEM.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global"
+ + " to android.provider.Settings.System, value is unchanged.");
+ return System.putStringForUser(resolver, name, value, tag,
+ makeDefault, userHandle, overrideableByRestore);
+ }
return sNameValueCache.putStringForUser(resolver, name, value, tag,
makeDefault, userHandle, overrideableByRestore);
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 1b38f59..5d84af0 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3584,6 +3584,23 @@
"content://telephony/carriers/enforce_managed");
/**
+ * The {@code content://} style URL for the perferred APN used for internet.
+ *
+ * @hide
+ */
+ public static final Uri PREFERRED_APN_URI = Uri.parse(
+ "content://telephony/carriers/preferapn/subId/");
+
+ /**
+ * The {@code content://} style URL for the perferred APN set id.
+ *
+ * @hide
+ */
+ public static final Uri PREFERRED_APN_SET_URI = Uri.parse(
+ "content://telephony/carriers/preferapnset/subId/");
+
+
+ /**
* The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
* @hide
*/
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/security/attestationverification/AttestationProfile.aidl
similarity index 78%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to core/java/android/security/attestationverification/AttestationProfile.aidl
index 3729c09..51696a9 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/java/android/security/attestationverification/AttestationProfile.aidl
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package android.window;
+package android.security.attestationverification;
/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
+ * {@hide}
*/
-parcelable TaskFragmentAppearedInfo;
+parcelable AttestationProfile;
diff --git a/core/java/android/security/attestationverification/AttestationProfile.java b/core/java/android/security/attestationverification/AttestationProfile.java
new file mode 100644
index 0000000..7a43dac
--- /dev/null
+++ b/core/java/android/security/attestationverification/AttestationProfile.java
@@ -0,0 +1,296 @@
+/*
+ * 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 android.security.attestationverification;
+
+import static android.security.attestationverification.AttestationVerificationManager.PROFILE_APP_DEFINED;
+import static android.security.attestationverification.AttestationVerificationManager.PROFILE_UNKNOWN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+import android.security.attestationverification.AttestationVerificationManager.AttestationProfileId;
+import android.util.Log;
+
+import com.android.internal.util.DataClass;
+
+
+/**
+ * An attestation profile defining the security requirements for verifying the attestation of a
+ * remote compute environment.
+ *
+ * <p>This class is immutable and thread-safe. When checking this profile against an expected
+ * profile, it is recommended to construct the expected profile and compare them with {@code
+ * equals()}.
+ *
+ * @hide
+ * @see AttestationVerificationManager
+ */
+@DataClass(
+ genConstructor = false,
+ genEqualsHashCode = true
+)
+public final class AttestationProfile implements Parcelable {
+
+ private static final String TAG = "AVF";
+
+ /**
+ * The ID of a system-defined attestation profile.
+ *
+ * See constants in {@link AttestationVerificationManager} prefixed with {@code PROFILE_}. If
+ * this has the value of {@link AttestationVerificationManager#PROFILE_APP_DEFINED}, then the
+ * packageName and profileName are non-null.
+ */
+ @AttestationProfileId
+ private final int mAttestationProfileId;
+
+ /**
+ * The package name of a app-defined attestation profile.
+ *
+ * This value will be null unless the value of attestationProfileId is {@link
+ * AttestationVerificationManager#PROFILE_APP_DEFINED}.
+ */
+ @Nullable
+ private final String mPackageName;
+
+
+ /**
+ * The name of an app-defined attestation profile.
+ *
+ * This value will be null unless the value of attestationProfileId is {@link
+ * AttestationVerificationManager#PROFILE_APP_DEFINED}.
+ */
+ @Nullable
+ private final String mProfileName;
+
+ private AttestationProfile(
+ @AttestationProfileId int attestationProfileId,
+ @Nullable String packageName,
+ @Nullable String profileName) {
+ mAttestationProfileId = attestationProfileId;
+ mPackageName = packageName;
+ mProfileName = profileName;
+ }
+
+ /**
+ * Create a profile with the given id.
+ *
+ * <p>This constructor is for specifying a profile which is defined by the system. These are
+ * available as constants in the {@link AttestationVerificationManager} class prefixed with
+ * {@code PROFILE_}.
+ *
+ * @param attestationProfileId the ID of the system-defined profile
+ * @throws IllegalArgumentException when called with
+ * {@link AttestationVerificationManager#PROFILE_APP_DEFINED}
+ * (use {@link #AttestationProfile(String, String)})
+ */
+ public AttestationProfile(@AttestationProfileId int attestationProfileId) {
+ this(attestationProfileId, null, null);
+ if (attestationProfileId == PROFILE_APP_DEFINED) {
+ throw new IllegalArgumentException("App-defined profiles must be specified with the "
+ + "constructor AttestationProfile#constructor(String, String)");
+ }
+ }
+
+ /**
+ * Create a profile with the given package name and profile name.
+ *
+ * <p>This constructor is for specifying a profile defined by an app. The packageName must
+ * match the package name of the app that defines the profile (as specified in the {@code
+ * package} attribute of the {@code
+ * <manifest>} tag in the app's manifest. The profile name matches the {@code name} attribute
+ * of the {@code <attestation-profile>} tag.
+ *
+ * <p>Apps must declare profiles in their manifest as an {@code <attestation-profile>} element.
+ * However, this constructor does not verify that such a profile exists. If the profile does not
+ * exist, verifications will fail.
+ *
+ * @param packageName the package name of the app defining the profile
+ * @param profileName the name of the profile
+ */
+ public AttestationProfile(@NonNull String packageName, @NonNull String profileName) {
+ this(PROFILE_APP_DEFINED, packageName, profileName);
+ if (packageName == null || profileName == null) {
+ throw new IllegalArgumentException("Both packageName and profileName must be non-null");
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (mAttestationProfileId == PROFILE_APP_DEFINED) {
+ return "AttestationProfile(package=" + mPackageName + ", name=" + mProfileName + ")";
+ } else {
+ String humanReadableProfileId;
+ switch (mAttestationProfileId) {
+ case PROFILE_UNKNOWN:
+ humanReadableProfileId = "PROFILE_UNKNOWN";
+ break;
+ default:
+ Log.e(TAG, "ERROR: Missing case in AttestationProfile#toString");
+ humanReadableProfileId = "ERROR";
+ }
+ return "AttestationProfile(" + humanReadableProfileId + "/" + mAttestationProfileId
+ + ")";
+ }
+ }
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/security
+ // /attestationverification/AttestationProfile.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * The ID of a system-defined attestation profile.
+ *
+ * See constants in {@link AttestationVerificationManager} prefixed with {@code PROFILE_}. If
+ * this has the value of {@link AttestationVerificationManager#PROFILE_APP_DEFINED}, then the
+ * packageName and profileName are non-null.
+ */
+ @DataClass.Generated.Member
+ public @AttestationProfileId int getAttestationProfileId() {
+ return mAttestationProfileId;
+ }
+
+ /**
+ * The package name of a app-defined attestation profile.
+ *
+ * This value will be null unless the value of attestationProfileId is {@link
+ * AttestationVerificationManager#PROFILE_APP_DEFINED}.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * The name of an app-defined attestation profile.
+ *
+ * This value will be null unless the value of attestationProfileId is {@link
+ * AttestationVerificationManager#PROFILE_APP_DEFINED}.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getProfileName() {
+ return mProfileName;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(AttestationProfile other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ AttestationProfile that = (AttestationProfile) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mAttestationProfileId == that.mAttestationProfileId
+ && java.util.Objects.equals(mPackageName, that.mPackageName)
+ && java.util.Objects.equals(mProfileName, that.mProfileName);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mAttestationProfileId;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mProfileName);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mPackageName != null) flg |= 0x2;
+ if (mProfileName != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeInt(mAttestationProfileId);
+ if (mPackageName != null) dest.writeString(mPackageName);
+ if (mProfileName != null) dest.writeString(mProfileName);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ AttestationProfile(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ int attestationProfileId = in.readInt();
+ String packageName = (flg & 0x2) == 0 ? null : in.readString();
+ String profileName = (flg & 0x4) == 0 ? null : in.readString();
+
+ this.mAttestationProfileId = attestationProfileId;
+ com.android.internal.util.AnnotationValidations.validate(
+ AttestationProfileId.class, null, mAttestationProfileId);
+ this.mPackageName = packageName;
+ this.mProfileName = profileName;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<AttestationProfile> CREATOR
+ = new Parcelable.Creator<AttestationProfile>() {
+ @Override
+ public AttestationProfile[] newArray(int size) {
+ return new AttestationProfile[size];
+ }
+
+ @Override
+ public AttestationProfile createFromParcel(@NonNull android.os.Parcel in) {
+ return new AttestationProfile(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1633629498403L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/security/attestationverification/AttestationProfile.java",
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.security.attestationverification.AttestationVerificationManager.AttestationProfileId int mAttestationProfileId\nprivate final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mProfileName\npublic @java.lang.Override java.lang.String toString()\nclass AttestationProfile extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/security/attestationverification/AttestationVerificationManager.java b/core/java/android/security/attestationverification/AttestationVerificationManager.java
new file mode 100644
index 0000000..db783ce
--- /dev/null
+++ b/core/java/android/security/attestationverification/AttestationVerificationManager.java
@@ -0,0 +1,324 @@
+/*
+ * 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 android.security.attestationverification;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ParcelDuration;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.time.Duration;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+
+/**
+ * Provides methods for verifying that attestations from remote compute environments meet minimum
+ * security requirements specified by attestation profiles.
+ *
+ * @hide
+ */
+@SystemService(Context.ATTESTATION_VERIFICATION_SERVICE)
+public class AttestationVerificationManager {
+
+ private static final String TAG = "AVF";
+ private static final Duration MAX_TOKEN_AGE = Duration.ofHours(1);
+
+ private final Context mContext;
+ private final IAttestationVerificationManagerService mService;
+
+ /**
+ * Verifies that {@code attestation} describes a computing environment that meets the
+ * requirements of {@code profile}, {@code localBindingType}, and {@code requirements}.
+ *
+ * <p>This method verifies that at least one system-registered {@linkplain
+ * AttestationVerificationService attestation verifier} associated with {@code profile} and
+ * {@code localBindingType} has verified that {@code attestation} attests that the remote
+ * environment matching the local binding data (determined by {@code localBindingType}) in
+ * {@code requirements} meets the requirements of the profile.
+ *
+ * <p>For successful verification, the {@code requirements} bundle must contain locally-known
+ * data which must match {@code attestation}. The required data in the bundle is defined by the
+ * {@code localBindingType} (see documentation for the type). Verifiers will fail to verify the
+ * attestation if the bundle contains unsupported data.
+ *
+ * <p>The {@code localBindingType} specifies how {@code attestation} is bound to a local
+ * secure channel endpoint or similar connection with the target remote environment described by
+ * the attestation. The binding is expected to be related to a cryptographic protocol, and each
+ * binding type requires specific arguments to be present in the {@code requirements} bundle. It
+ * is this binding to something known locally that ensures an attestation is not only valid, but
+ * is also associated with a particular connection.
+ *
+ * <p>The {@code callback} is called with a result and {@link VerificationToken} (which may be
+ * null). The result is an integer (see constants in this class with the prefix {@code RESULT_}.
+ * The result is {@link #RESULT_SUCCESS} when at least one verifier has passed its checks. The
+ * token may be used in calls to other parts of the system.
+ *
+ * <p>It's expected that a verifier will be able to decode and understand the passed values,
+ * otherwise fail to verify. {@code attestation} should contain some type data to prevent parse
+ * errors.
+ *
+ * <p>The values put into the {@code requirements} Bundle depend on the {@code
+ * localBindingType} used.
+ *
+ * @param profile the attestation profile which defines the security requirements which
+ * must be met by the environment described by {@code attestation}
+ * @param localBindingType the type of the local binding data; see constants in this class with
+ * the prefix {@code TYPE_}
+ * @param requirements a {@link Bundle} containing locally-known data which must match
+ * {@code attestation}
+ * @param attestation attestation data which describes a remote computing environment
+ * @param executor {@code callback} will be executed on this executor
+ * @param callback will be called with the results of the verification
+ * @see AttestationVerificationService
+ */
+ @RequiresPermission(Manifest.permission.USE_ATTESTATION_VERIFICATION_SERVICE)
+ public void verifyAttestation(
+ @NonNull AttestationProfile profile,
+ @LocalBindingType int localBindingType,
+ @NonNull Bundle requirements,
+ @NonNull byte[] attestation,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiConsumer<@VerificationResult Integer, VerificationToken> callback) {
+ try {
+ AndroidFuture<IVerificationResult> resultCallback = new AndroidFuture<>();
+ resultCallback.thenAccept(result -> {
+ Log.d(TAG, "verifyAttestation result: " + result.resultCode + " / " + result.token);
+ executor.execute(() -> {
+ callback.accept(result.resultCode, result.token);
+ });
+ });
+
+ mService.verifyAttestation(profile, localBindingType, requirements, attestation,
+ resultCallback);
+
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Verifies that {@code token} is a valid token, returning the result contained in valid
+ * tokens.
+ *
+ * <p>This verifies that the token was issued by the platform and thus the system verified
+ * attestation data against the specified {@code profile}, {@code localBindingType}, and {@code
+ * requirements}. The value returned by this method is the same as the one originally returned
+ * when the token was generated. Callers of this method should not trust the provider of the
+ * token to also specify the profile, local binding type, or requirements, but instead have
+ * their own security requirements about these arguments.
+ *
+ * <p>This method, in contrast to {@code verifyAttestation}, executes synchronously and only
+ * checks that a previous verification succeeded. This allows callers to pass the token to
+ * others, including system APIs, without those components needing to re-verify the attestation
+ * data, an operation which can take several seconds.
+ *
+ * <p>When {@code maximumAge} is not specified (null), this method verifies the token was
+ * generated in the past hour. Otherwise, it verifies the token was generated between now and
+ * {@code maximumAge} ago. The maximum value of {@code maximumAge} is one hour; specifying a
+ * duration greater than one hour will result in an {@link IllegalArgumentException}.
+ *
+ * @param profile the attestation profile which must be in the token
+ * @param localBindingType the local binding type which must be in the token
+ * @param requirements the requirements which must be in the token
+ * @param token the token to be verified
+ * @param maximumAge the maximum age to accept for the token
+ */
+ @RequiresPermission(Manifest.permission.USE_ATTESTATION_VERIFICATION_SERVICE)
+ @CheckResult
+ @VerificationResult
+ public int verifyToken(
+ @NonNull AttestationProfile profile,
+ @LocalBindingType int localBindingType,
+ @NonNull Bundle requirements,
+ @NonNull VerificationToken token,
+ @Nullable Duration maximumAge) {
+ Duration usedMaximumAge;
+ if (maximumAge == null) {
+ usedMaximumAge = MAX_TOKEN_AGE;
+ } else {
+ if (maximumAge.compareTo(MAX_TOKEN_AGE) > 0) {
+ throw new IllegalArgumentException(
+ "maximumAge cannot be greater than " + MAX_TOKEN_AGE + "; was "
+ + maximumAge);
+ }
+ usedMaximumAge = maximumAge;
+ }
+
+ try {
+ AndroidFuture<Integer> resultCallback = new AndroidFuture<>();
+ resultCallback.orTimeout(5, TimeUnit.SECONDS);
+
+ mService.verifyToken(token, new ParcelDuration(usedMaximumAge), resultCallback);
+ return resultCallback.get(); // block on result callback
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (Throwable t) {
+ throw new RuntimeException("Error verifying token.", t);
+ }
+ }
+
+ /** @hide */
+ public AttestationVerificationManager(
+ @NonNull Context context,
+ @NonNull IAttestationVerificationManagerService service) {
+ this.mContext = context;
+ this.mService = service;
+ }
+
+ /** @hide */
+ @IntDef(
+ prefix = {"PROFILE_"},
+ value = {
+ PROFILE_UNKNOWN,
+ PROFILE_APP_DEFINED,
+ PROFILE_SELF_TRUSTED,
+ PROFILE_PEER_DEVICE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttestationProfileId {
+ }
+
+ /**
+ * The profile is unknown because it is a profile unknown to this version of the SDK.
+ */
+ public static final int PROFILE_UNKNOWN = 0;
+
+ /** The profile is defined by an app. */
+ public static final int PROFILE_APP_DEFINED = 1;
+
+ /**
+ * A system-defined profile which verifies that the attesting environment can create an
+ * attestation with the same root certificate as the verifying device with a matching
+ * attestation challenge.
+ *
+ * This profile is intended to be used only for testing.
+ */
+ public static final int PROFILE_SELF_TRUSTED = 2;
+
+ /**
+ * A system-defined profile which verifies that the attesting environment environment is similar
+ * to the current device in terms of security model and security configuration. This category is
+ * fairly broad and most securely configured Android devices should qualify, along with a
+ * variety of non-Android devices.
+ */
+ public static final int PROFILE_PEER_DEVICE = 3;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"TYPE_"},
+ value = {
+ TYPE_UNKNOWN,
+ TYPE_APP_DEFINED,
+ TYPE_PUBLIC_KEY,
+ TYPE_CHALLENGE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LocalBindingType {
+ }
+
+ /**
+ * The type of the local binding data is unknown because it is a type unknown to this version of
+ * the SDK.
+ */
+ public static final int TYPE_UNKNOWN = 0;
+
+ /**
+ * A local binding type for app-defined profiles which use local binding data which does not
+ * match any of the existing system-defined types.
+ */
+ public static final int TYPE_APP_DEFINED = 1;
+
+ /**
+ * A local binding type where the attestation is bound to a public key negotiated and
+ * authenticated to a public key.
+ *
+ * <p>When using this type, the {@code requirements} bundle contains values for:
+ * <ul>
+ * <li>{@link #PARAM_PUBLIC_KEY}
+ * <li>{@link #PARAM_ID}: identifying the remote environment, optional
+ * </ul>
+ */
+ public static final int TYPE_PUBLIC_KEY = 2;
+
+ /**
+ * A local binding type where the attestation is bound to a challenge.
+ *
+ * <p>When using this type, the {@code requirements} bundle contains values for:
+ * <ul>
+ * <li>{@link #PARAM_CHALLENGE}: containing the challenge
+ * </ul>
+ */
+ public static final int TYPE_CHALLENGE = 3;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"RESULT_"},
+ value = {
+ RESULT_UNKNOWN,
+ RESULT_SUCCESS,
+ RESULT_FAILURE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface VerificationResult {
+ }
+
+ /** The result of the verification is unknown because it has a value unknown to this SDK. */
+ public static final int RESULT_UNKNOWN = 0;
+
+ /** The result of the verification was successful. */
+ public static final int RESULT_SUCCESS = 1;
+
+ /**
+ * The result of the attestation verification was failure. The attestation could not be
+ * verified.
+ */
+ public static final int RESULT_FAILURE = 2;
+
+ /**
+ * Requirements bundle parameter key for a public key, a byte array.
+ *
+ * <p>This should contain the encoded key bytes according to the ASN.1 type
+ * {@code SubjectPublicKeyInfo} defined in the X.509 standard, the same as a call to {@link
+ * java.security.spec.X509EncodedKeySpec#getEncoded()} would produce.
+ *
+ * @see Bundle#putByteArray(String, byte[])
+ */
+ public static final String PARAM_PUBLIC_KEY = "localbinding.public_key";
+
+ /** Requirements bundle parameter key for an ID, String. */
+ public static final String PARAM_ID = "localbinding.id";
+
+ /** Requirements bundle parameter for a challenge. */
+ public static final String PARAM_CHALLENGE = "localbinding.challenge";
+}
diff --git a/core/java/android/security/attestationverification/AttestationVerificationService.java b/core/java/android/security/attestationverification/AttestationVerificationService.java
new file mode 100644
index 0000000..26c3051
--- /dev/null
+++ b/core/java/android/security/attestationverification/AttestationVerificationService.java
@@ -0,0 +1,100 @@
+/*
+ * 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 android.security.attestationverification;
+
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.app.Service;
+import android.os.Bundle;
+import android.security.attestationverification.AttestationVerificationManager.VerificationResult;
+
+/**
+ * A verifier which can be implemented by apps to verify an attestation (as described in {@link
+ * AttestationVerificationManager}).
+ *
+ * In the manifest for this service, specify the profile and local binding type this verifier
+ * supports. Create a new service for each combination of profile & local binding type that your app
+ * supports. Each service must declare an {@code intent-filter} action of {@link #SERVICE_INTERFACE}
+ * and permission of {@link android.Manifest.permission#BIND_ATTESTATION_VERIFICATION_SERVICE}.
+ *
+ * <p>Example:
+ * {@code
+ * <pre>
+ * <service android:name=".MyAttestationVerificationService"
+ * android:permission="android.permission.BIND_ATTESTATION_VERIFICATION_SERVICE"
+ * android:exported="true">
+ * <intent-filter>
+ * <action
+ * android:name="android.security.attestationverification.AttestationVerificationService" />
+ * </intent-filter>
+ * <meta-data android:name="android.security.attestationverification.PROFILE_ID"
+ * android:value="PROFILE_PLACEHOLDER_0" />
+ * <meta-data android:name="android.security.attestationverification.LOCAL_BINDING_TYPE"
+ * android:value="TYPE_PLACEHOLDER_0" />
+ * </service>
+ * </pre>
+ * }
+ *
+ * <p>For app-defined profiles, an example of the {@code <meta-data>}:
+ * {@code
+ * <pre>
+ * <meta-data android:name="android.security.attestation.PROFILE_PACKAGE_NAME"
+ * android:value="com.example" />
+ * <meta-data android:name="android.security.attestation.PROFILE_NAME"
+ * android:value="com.example.profile.PROFILE_FOO" />
+ * </pre>
+ * }
+ *
+ * @hide
+ */
+public abstract class AttestationVerificationService extends Service {
+
+ /**
+ * An intent action for a service to be bound and act as an attestation verifier.
+ *
+ * <p>The app will be kept alive for a short duration between verification calls after which
+ * the system will unbind from this service making the app eligible for cleanup.
+ *
+ * <p>The service must also require permission
+ * {@link android.Manifest.permission#BIND_ATTESTATION_VERIFICATION_SERVICE}.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.security.attestationverification.AttestationVerificationService";
+
+ /**
+ * Verifies that {@code attestation} attests that the device identified by the local binding
+ * data in {@code requirements} meets the minimum requirements of this verifier for this
+ * verifier's profile.
+ *
+ * <p>Called by the system to verify an attestation.
+ *
+ * <p>The data passed into this method comes directly from apps and should be treated as
+ * potentially dangerous user input.
+ *
+ * @param requirements a {@link Bundle} containing locally-known data which must match {@code
+ * attestation}
+ * @param attestation the attestation to verify
+ * @return whether the verification passed
+ * @see AttestationVerificationManager#verifyAttestation(AttestationProfile, int, Bundle,
+ * byte[], java.util.concurrent.Executor, java.util.function.BiConsumer)
+ */
+ @CheckResult
+ @VerificationResult
+ public abstract int onVerifyPeerDeviceAttestation(
+ @NonNull Bundle requirements,
+ @NonNull byte[] attestation);
+}
diff --git a/core/java/android/security/attestationverification/IAttestationVerificationManagerService.aidl b/core/java/android/security/attestationverification/IAttestationVerificationManagerService.aidl
new file mode 100644
index 0000000..2fb328c
--- /dev/null
+++ b/core/java/android/security/attestationverification/IAttestationVerificationManagerService.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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 android.security.attestationverification;
+
+import android.os.Bundle;
+import android.os.ParcelDuration;
+import android.security.attestationverification.AttestationProfile;
+import android.security.attestationverification.VerificationToken;
+import com.android.internal.infra.AndroidFuture;
+
+
+/**
+ * Binder interface to communicate with AttestationVerificationManagerService.
+ * @hide
+ */
+oneway interface IAttestationVerificationManagerService {
+
+ void verifyAttestation(
+ in AttestationProfile profile,
+ in int localBindingType,
+ in Bundle requirements,
+ in byte[] attestation,
+ in AndroidFuture resultCallback);
+
+ void verifyToken(
+ in VerificationToken token,
+ in ParcelDuration maximumTokenAge,
+ in AndroidFuture resultCallback);
+}
diff --git a/core/java/android/security/attestationverification/IAttestationVerificationService.aidl b/core/java/android/security/attestationverification/IAttestationVerificationService.aidl
new file mode 100644
index 0000000..082ad32
--- /dev/null
+++ b/core/java/android/security/attestationverification/IAttestationVerificationService.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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 android.security.attestationverification;
+
+import android.os.Bundle;
+import com.android.internal.infra.AndroidFuture;
+
+
+/**
+ * Binder interface for the system server to communicate with app implementations of
+ * AttestationVerificationService.
+ * @hide
+ */
+oneway interface IAttestationVerificationService {
+ void onVerifyAttestation(
+ in Bundle requirements,
+ in byte[] attestation,
+ in AndroidFuture callback);
+}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/security/attestationverification/IVerificationResult.aidl
similarity index 61%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to core/java/android/security/attestationverification/IVerificationResult.aidl
index 3729c09..f61c456 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/java/android/security/attestationverification/IVerificationResult.aidl
@@ -14,10 +14,19 @@
* limitations under the License.
*/
-package android.window;
+package android.security.attestationverification;
+
+import android.security.attestationverification.VerificationToken;
+
/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
+ * The result of an attestation verification.
+ *
+ * {@hide}
*/
-parcelable TaskFragmentAppearedInfo;
+parcelable IVerificationResult {
+ /** The result code corresponding to @VerificationResult. */
+ int resultCode;
+ /** The token for the verification or null. */
+ VerificationToken token;
+}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/security/attestationverification/VerificationToken.aidl
similarity index 78%
rename from core/java/android/window/TaskFragmentAppearedInfo.aidl
rename to core/java/android/security/attestationverification/VerificationToken.aidl
index 3729c09..666a8b0 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/java/android/security/attestationverification/VerificationToken.aidl
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package android.window;
+package android.security.attestationverification;
/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
+ * {@hide}
*/
-parcelable TaskFragmentAppearedInfo;
+parcelable VerificationToken;
diff --git a/core/java/android/security/attestationverification/VerificationToken.java b/core/java/android/security/attestationverification/VerificationToken.java
new file mode 100644
index 0000000..ae26823
--- /dev/null
+++ b/core/java/android/security/attestationverification/VerificationToken.java
@@ -0,0 +1,523 @@
+/*
+ * 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 android.security.attestationverification;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.security.attestationverification.AttestationVerificationManager.LocalBindingType;
+import android.security.attestationverification.AttestationVerificationManager.VerificationResult;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInstant;
+
+import java.time.Duration;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+
+/**
+ * Token representing the result of an attestation verification, which can be passed to other parts
+ * of the OS or other apps as proof of the verification.
+ *
+ * Tokens are only valid within the same UID (which means within a single app unless the deprecated
+ * android:sharedUserId manifest value is used).
+ *
+ * @hide
+ * @see Bundle#putParcelable(String, Parcelable)
+ */
+@DataClass(
+ genConstructor = false,
+ genHiddenBuilder = true
+)
+public final class VerificationToken implements Parcelable {
+
+ /**
+ * The attestation profile which was used to perform the verification.
+ * @hide
+ */
+ @NonNull
+ private final AttestationProfile mAttestationProfile;
+
+ /**
+ * The local binding type of the local binding data used to perform the verification.
+ * @hide
+ */
+ @LocalBindingType
+ private final int mLocalBindingType;
+
+ /**
+ * The requirements used to perform the verification.
+ * @hide
+ */
+ @NonNull
+ private final Bundle mRequirements;
+
+ /**
+ * The result of the {@link AttestationVerificationManager#verifyAttestation(int, int, Bundle,
+ * byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token holders from
+ * accidentally reading this value without calling {@code verifyToken}. Do <b>not</b> use this
+ * value directly; call {@link AttestationVerificationManager#verifyToken(VerificationToken,
+ * Duration)} to verify a valid token and it will return this value.
+ *
+ * If the token is valid, this value is returned directly by {#verifyToken}.
+ *
+ * @hide
+ */
+ @VerificationResult
+ private final int mVerificationResult;
+
+ /**
+ * Time when the token was generated, set by the system.
+ */
+ @NonNull
+ @DataClass.ParcelWith(ForInstant.class)
+ private final java.time.Instant mVerificationTime;
+
+ /**
+ * A Hash-based message authentication code used to verify the contents and authenticity of the
+ * rest of the token. The hash is created using a secret key known only to the system server.
+ * When verifying the token, the system re-hashes the token and verifies the generated HMAC is
+ * the same.
+ *
+ * @hide
+ */
+ @NonNull
+ private final byte[] mHmac;
+
+ /**
+ * The UID of the process which called {@code verifyAttestation} to create the token, as
+ * returned by {@link Binder#getCallingUid()}. Calls to {@code verifyToken} will fail if the UID
+ * of calling process does not match this value. This ensures that tokens cannot be shared
+ * between UIDs.
+ *
+ * @hide
+ */
+ private int mUid;
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/security/attestationverification/VerificationToken.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ VerificationToken(
+ @NonNull AttestationProfile attestationProfile,
+ @LocalBindingType int localBindingType,
+ @NonNull Bundle requirements,
+ @VerificationResult int verificationResult,
+ @NonNull java.time.Instant verificationTime,
+ @NonNull byte[] hmac,
+ int uid) {
+ this.mAttestationProfile = attestationProfile;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAttestationProfile);
+ this.mLocalBindingType = localBindingType;
+ com.android.internal.util.AnnotationValidations.validate(
+ LocalBindingType.class, null, mLocalBindingType);
+ this.mRequirements = requirements;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mRequirements);
+ this.mVerificationResult = verificationResult;
+ com.android.internal.util.AnnotationValidations.validate(
+ VerificationResult.class, null, mVerificationResult);
+ this.mVerificationTime = verificationTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mVerificationTime);
+ this.mHmac = hmac;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHmac);
+ this.mUid = uid;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The attestation profile which was used to perform the verification.
+ */
+ @DataClass.Generated.Member
+ public @NonNull AttestationProfile getAttestationProfile() {
+ return mAttestationProfile;
+ }
+
+ /**
+ * The local binding type of the local binding data used to perform the verification.
+ */
+ @DataClass.Generated.Member
+ public @LocalBindingType int getLocalBindingType() {
+ return mLocalBindingType;
+ }
+
+ /**
+ * The requirements used to perform the verification.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Bundle getRequirements() {
+ return mRequirements;
+ }
+
+ /**
+ * The result of the {@link AttestationVerificationManager#verifyAttestation(int, int, Bundle,
+ * byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token holders from
+ * accidentally reading this value without calling {@code verifyToken}. Do <b>not</b> use this
+ * value directly; call {@link AttestationVerificationManager#verifyToken(VerificationToken,
+ * Duration)} to verify a valid token and it will return this value.
+ *
+ * If the token is valid, this value is returned directly by {#verifyToken}.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @VerificationResult int getVerificationResult() {
+ return mVerificationResult;
+ }
+
+ /**
+ * Time when the token was generated, set by the system.
+ */
+ @DataClass.Generated.Member
+ public @NonNull java.time.Instant getVerificationTime() {
+ return mVerificationTime;
+ }
+
+ /**
+ * A Hash-based message authentication code used to verify the contents and authenticity of the
+ * rest of the token. The hash is created using a secret key known only to the system server.
+ * When verifying the token, the system re-hashes the token and verifies the generated HMAC is
+ * the same.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull byte[] getHmac() {
+ return mHmac;
+ }
+
+ /**
+ * The UID of the process which called {@code verifyAttestation} to create the token, as
+ * returned by {@link Binder#getCallingUid()}. Calls to {@code verifyToken} will fail if the UID
+ * of calling process does not match this value. This ensures that tokens cannot be shared
+ * between UIDs.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public int getUid() {
+ return mUid;
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<java.time.Instant> sParcellingForVerificationTime =
+ Parcelling.Cache.get(
+ ForInstant.class);
+ static {
+ if (sParcellingForVerificationTime == null) {
+ sParcellingForVerificationTime = Parcelling.Cache.put(
+ new ForInstant());
+ }
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeTypedObject(mAttestationProfile, flags);
+ dest.writeInt(mLocalBindingType);
+ dest.writeBundle(mRequirements);
+ dest.writeInt(mVerificationResult);
+ sParcellingForVerificationTime.parcel(mVerificationTime, dest, flags);
+ dest.writeByteArray(mHmac);
+ dest.writeInt(mUid);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ VerificationToken(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ AttestationProfile attestationProfile = (AttestationProfile) in.readTypedObject(AttestationProfile.CREATOR);
+ int localBindingType = in.readInt();
+ Bundle requirements = in.readBundle();
+ int verificationResult = in.readInt();
+ java.time.Instant verificationTime = sParcellingForVerificationTime.unparcel(in);
+ byte[] hmac = in.createByteArray();
+ int uid = in.readInt();
+
+ this.mAttestationProfile = attestationProfile;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAttestationProfile);
+ this.mLocalBindingType = localBindingType;
+ com.android.internal.util.AnnotationValidations.validate(
+ LocalBindingType.class, null, mLocalBindingType);
+ this.mRequirements = requirements;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mRequirements);
+ this.mVerificationResult = verificationResult;
+ com.android.internal.util.AnnotationValidations.validate(
+ VerificationResult.class, null, mVerificationResult);
+ this.mVerificationTime = verificationTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mVerificationTime);
+ this.mHmac = hmac;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHmac);
+ this.mUid = uid;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<VerificationToken> CREATOR
+ = new Parcelable.Creator<VerificationToken>() {
+ @Override
+ public VerificationToken[] newArray(int size) {
+ return new VerificationToken[size];
+ }
+
+ @Override
+ public VerificationToken createFromParcel(@NonNull android.os.Parcel in) {
+ return new VerificationToken(in);
+ }
+ };
+
+ /**
+ * A builder for {@link VerificationToken}
+ * @hide
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @NonNull AttestationProfile mAttestationProfile;
+ private @LocalBindingType int mLocalBindingType;
+ private @NonNull Bundle mRequirements;
+ private @VerificationResult int mVerificationResult;
+ private @NonNull java.time.Instant mVerificationTime;
+ private @NonNull byte[] mHmac;
+ private int mUid;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param attestationProfile
+ * The attestation profile which was used to perform the verification.
+ * @param localBindingType
+ * The local binding type of the local binding data used to perform the verification.
+ * @param requirements
+ * The requirements used to perform the verification.
+ * @param verificationResult
+ * The result of the {@link AttestationVerificationManager#verifyAttestation(int, int, Bundle,
+ * byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token holders from
+ * accidentally reading this value without calling {@code verifyToken}. Do <b>not</b> use this
+ * value directly; call {@link AttestationVerificationManager#verifyToken(VerificationToken,
+ * Duration)} to verify a valid token and it will return this value.
+ *
+ * If the token is valid, this value is returned directly by {#verifyToken}.
+ * @param verificationTime
+ * Time when the token was generated, set by the system.
+ * @param hmac
+ * A Hash-based message authentication code used to verify the contents and authenticity of the
+ * rest of the token. The hash is created using a secret key known only to the system server.
+ * When verifying the token, the system re-hashes the token and verifies the generated HMAC is
+ * the same.
+ * @param uid
+ * The UID of the process which called {@code verifyAttestation} to create the token, as
+ * returned by {@link Binder#getCallingUid()}. Calls to {@code verifyToken} will fail if the UID
+ * of calling process does not match this value. This ensures that tokens cannot be shared
+ * between UIDs.
+ */
+ public Builder(
+ @NonNull AttestationProfile attestationProfile,
+ @LocalBindingType int localBindingType,
+ @NonNull Bundle requirements,
+ @VerificationResult int verificationResult,
+ @NonNull java.time.Instant verificationTime,
+ @NonNull byte[] hmac,
+ int uid) {
+ mAttestationProfile = attestationProfile;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAttestationProfile);
+ mLocalBindingType = localBindingType;
+ com.android.internal.util.AnnotationValidations.validate(
+ LocalBindingType.class, null, mLocalBindingType);
+ mRequirements = requirements;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mRequirements);
+ mVerificationResult = verificationResult;
+ com.android.internal.util.AnnotationValidations.validate(
+ VerificationResult.class, null, mVerificationResult);
+ mVerificationTime = verificationTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mVerificationTime);
+ mHmac = hmac;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHmac);
+ mUid = uid;
+ }
+
+ /**
+ * The attestation profile which was used to perform the verification.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAttestationProfile(@NonNull AttestationProfile value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mAttestationProfile = value;
+ return this;
+ }
+
+ /**
+ * The local binding type of the local binding data used to perform the verification.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setLocalBindingType(@LocalBindingType int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mLocalBindingType = value;
+ return this;
+ }
+
+ /**
+ * The requirements used to perform the verification.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setRequirements(@NonNull Bundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mRequirements = value;
+ return this;
+ }
+
+ /**
+ * The result of the {@link AttestationVerificationManager#verifyAttestation(int, int, Bundle,
+ * byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token holders from
+ * accidentally reading this value without calling {@code verifyToken}. Do <b>not</b> use this
+ * value directly; call {@link AttestationVerificationManager#verifyToken(VerificationToken,
+ * Duration)} to verify a valid token and it will return this value.
+ *
+ * If the token is valid, this value is returned directly by {#verifyToken}.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setVerificationResult(@VerificationResult int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mVerificationResult = value;
+ return this;
+ }
+
+ /**
+ * Time when the token was generated, set by the system.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setVerificationTime(@NonNull java.time.Instant value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mVerificationTime = value;
+ return this;
+ }
+
+ /**
+ * A Hash-based message authentication code used to verify the contents and authenticity of the
+ * rest of the token. The hash is created using a secret key known only to the system server.
+ * When verifying the token, the system re-hashes the token and verifies the generated HMAC is
+ * the same.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setHmac(@NonNull byte... value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mHmac = value;
+ return this;
+ }
+
+ /**
+ * The UID of the process which called {@code verifyAttestation} to create the token, as
+ * returned by {@link Binder#getCallingUid()}. Calls to {@code verifyToken} will fail if the UID
+ * of calling process does not match this value. This ensures that tokens cannot be shared
+ * between UIDs.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setUid(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40;
+ mUid = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull VerificationToken build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80; // Mark builder used
+
+ VerificationToken o = new VerificationToken(
+ mAttestationProfile,
+ mLocalBindingType,
+ mRequirements,
+ mVerificationResult,
+ mVerificationTime,
+ mHmac,
+ mUid);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x80) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1633629747234L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/security/attestationverification/VerificationToken.java",
+ inputSignatures = "private final @android.annotation.NonNull android.security.attestationverification.AttestationProfile mAttestationProfile\nprivate final @android.security.attestationverification.AttestationVerificationManager.LocalBindingType int mLocalBindingType\nprivate final @android.annotation.NonNull android.os.Bundle mRequirements\nprivate final @android.security.attestationverification.AttestationVerificationManager.VerificationResult int mVerificationResult\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) java.time.Instant mVerificationTime\nprivate final @android.annotation.NonNull byte[] mHmac\nprivate int mUid\nclass VerificationToken extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genHiddenBuilder=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/security/attestationverification/package.html b/core/java/android/security/attestationverification/package.html
new file mode 100644
index 0000000..783d0a1
--- /dev/null
+++ b/core/java/android/security/attestationverification/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 88818b6..65a857e 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1470,10 +1470,10 @@
* Requests a list of supported actions from an app.
*
* @param activityId Ths activity id of the app to get the actions from.
- * @param resultExecutor The handler to receive the callback
* @param cancellationSignal A signal to cancel the operation in progress,
* or {@code null} if none.
- * @param callback The callback to receive the response
+ * @param resultExecutor The handler to receive the callback.
+ * @param callback The callback to receive the response.
*/
public final void requestDirectActions(@NonNull ActivityId activityId,
@Nullable CancellationSignal cancellationSignal,
diff --git a/core/java/android/util/BackupUtils.java b/core/java/android/util/BackupUtils.java
index 474ceda..4fcb13c 100644
--- a/core/java/android/util/BackupUtils.java
+++ b/core/java/android/util/BackupUtils.java
@@ -37,6 +37,10 @@
public BadVersionException(String message) {
super(message);
}
+
+ public BadVersionException(String message, Throwable throwable) {
+ super(message, throwable);
+ }
}
public static String readString(DataInputStream in) throws IOException {
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index ece6b35..bab2089 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -242,35 +242,49 @@
*
* @hide
*/
- public static String flagsToString(Class<?> clazz, String prefix, int flags) {
+ public static String flagsToString(Class<?> clazz, String prefix, long flags) {
final StringBuilder res = new StringBuilder();
boolean flagsWasZero = flags == 0;
for (Field field : clazz.getDeclaredFields()) {
final int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
- && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
- try {
- final int value = field.getInt(null);
- if (value == 0 && flagsWasZero) {
- return constNameWithoutPrefix(prefix, field);
- }
- if (value != 0 && (flags & value) == value) {
- flags &= ~value;
- res.append(constNameWithoutPrefix(prefix, field)).append('|');
- }
- } catch (IllegalAccessException ignored) {
+ && (field.getType().equals(int.class) || field.getType().equals(long.class))
+ && field.getName().startsWith(prefix)) {
+ final long value = getFieldValue(field);
+ if (value == 0 && flagsWasZero) {
+ return constNameWithoutPrefix(prefix, field);
+ }
+ if (value != 0 && (flags & value) == value) {
+ flags &= ~value;
+ res.append(constNameWithoutPrefix(prefix, field)).append('|');
}
}
}
if (flags != 0 || res.length() == 0) {
- res.append(Integer.toHexString(flags));
+ res.append(Long.toHexString(flags));
} else {
res.deleteCharAt(res.length() - 1);
}
return res.toString();
}
+ private static long getFieldValue(Field field) {
+ // Field could be int or long
+ try {
+ final long longValue = field.getLong(null);
+ if (longValue != 0) {
+ return longValue;
+ }
+ final int intValue = field.getInt(null);
+ if (intValue != 0) {
+ return intValue;
+ }
+ } catch (IllegalAccessException ignored) {
+ }
+ return 0;
+ }
+
/**
* Gets human-readable representation of constants (static final values).
*
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 9cd8313..de56d3a 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -151,8 +151,7 @@
if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId
&& mHandler.hasAccessibilityCallback(message)) {
AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid, /* initializeCache= */true)
- .setSameThreadMessage(message);
+ interrogatingTid).setSameThreadMessage(message);
} else {
// For messages without callback of interrogating client, just handle the
// message immediately if this is UI thread.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 8259a9d..3cc51c7 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -299,6 +299,15 @@
public static final int FLAG_OWN_DISPLAY_GROUP = 1 << 8;
/**
+ * Flag: Indicates that the display should always be unlocked. Only valid on virtual displays
+ * that aren't in the default display group.
+ *
+ * @hide
+ * @see #getFlags()
+ */
+ public static final int FLAG_ALWAYS_UNLOCKED = 1 << 9;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 6572510..b8614cc 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -863,6 +863,9 @@
if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
result.append(", FLAG_OWN_DISPLAY_GROUP");
}
+ if ((flags & Display.FLAG_ALWAYS_UNLOCKED) != 0) {
+ result.append(", FLAG_ALWAYS_UNLOCKED");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index ec613ed..c5bc99d 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -153,11 +153,20 @@
/**
* Invocation of the voice assistant via hardware button.
+ * This is a private constant. Feel free to renumber as desired.
* @hide
*/
public static final int ASSISTANT_BUTTON = 10002;
/**
+ * The user has performed a long press on the power button hardware that is resulting
+ * in an action being performed.
+ * This is a private constant. Feel free to renumber as desired.
+ * @hide
+ */
+ public static final int LONG_PRESS_POWER_BUTTON = 10003;
+
+ /**
* Flag for {@link View#performHapticFeedback(int, int)
* View.performHapticFeedback(int, int)}: Ignore the setting in the
* view for whether to perform haptic feedback, do it always.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 0b4857d..2c766bd 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -855,6 +855,23 @@
void attachWindowContextToWindowToken(IBinder clientToken, IBinder token);
/**
+ * Attaches a {@code clientToken} to associate with DisplayContent.
+ * <p>
+ * Note that this API should be invoked after calling
+ * {@link android.window.WindowTokenClient#attachContext(Context)}
+ * </p>
+ *
+ * @param clientToken {@link android.window.WindowContext#getWindowContextToken()
+ * the WindowContext's token}
+ * @param displayId The display associated with the window context
+ *
+ * @return the DisplayContent's {@link android.app.res.Configuration} if the Context is
+ * attached to the DisplayContent successfully. {@code null}, otherwise.
+ * @throws android.view.WindowManager.InvalidDisplayException if the display ID is invalid
+ */
+ Configuration attachToDisplayContent(IBinder clientToken, int displayId);
+
+ /**
* Detaches {@link android.window.WindowContext} from the window manager node it's currently
* attached to. It is no-op if the WindowContext is not attached to a window manager node.
*
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 02b2c5d..d609fb8 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -124,7 +124,12 @@
public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
int[] hideTypes) {
super.setControl(control, showTypes, hideTypes);
- if (control == null && !isRequestedVisibleAwaitingControl()) {
+ // TODO(b/204524304): clean-up how to deal with the timing issues of hiding IME:
+ // 1) Already requested show IME, in the meantime of WM callback the control but got null
+ // control when relayout comes first
+ // 2) Make sure no regression on some implicit request IME visibility calls (e.g.
+ // toggleSoftInput)
+ if (control == null && !mIsRequestedVisibleAwaitingControl) {
hide();
removeSurface();
}
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index aa1acc1..a6f88a7 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -709,8 +709,8 @@
}
/**
- * Queries the framework about whether any physical keys exist on the
- * any keyboard attached to the device that are capable of producing the given key code.
+ * Queries the framework about whether any physical keys exist on any currently attached input
+ * devices that are capable of producing the given key code.
*
* @param keyCode The key code to query.
* @return True if at least one attached keyboard supports the specified key code.
@@ -720,9 +720,8 @@
}
/**
- * Queries the framework about whether any physical keys exist on the
- * any keyboard attached to the device that are capable of producing the given
- * array of key codes.
+ * Queries the framework about whether any physical keys exist on any currently attached input
+ * devices that are capable of producing the given array of key codes.
*
* @param keyCodes The array of key codes to query.
* @return A new array of the same size as the key codes array whose elements
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 1460cb2..c3a638c 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -295,7 +295,8 @@
/** Key code constant: Fast Forward media key. */
public static final int KEYCODE_MEDIA_FAST_FORWARD = 90;
/** Key code constant: Mute key.
- * Mutes the microphone, unlike {@link #KEYCODE_VOLUME_MUTE}. */
+ * Mute key for the microphone (unlike {@link #KEYCODE_VOLUME_MUTE}, which is the speaker mute
+ * key). */
public static final int KEYCODE_MUTE = 91;
/** Key code constant: Page Up key. */
public static final int KEYCODE_PAGE_UP = 92;
@@ -482,9 +483,10 @@
/** Key code constant: Numeric keypad ')' key. */
public static final int KEYCODE_NUMPAD_RIGHT_PAREN = 163;
/** Key code constant: Volume Mute key.
- * Mutes the speaker, unlike {@link #KEYCODE_MUTE}.
- * This key should normally be implemented as a toggle such that the first press
- * mutes the speaker and the second press restores the original volume. */
+ * Mute key for speaker (unlike {@link #KEYCODE_MUTE}, which is the mute key for the
+ * microphone). This key should normally be implemented as a toggle such that the first press
+ * mutes the speaker and the second press restores the original volume.
+ */
public static final int KEYCODE_VOLUME_MUTE = 164;
/** Key code constant: Info key.
* Common on TV remotes to show additional information related to what is
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 7af77ca..00754af 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -613,7 +613,10 @@
// If there's no arguments, eg 'dumpsys gfxinfo', then dump everything.
// If there's a targetted package, eg 'dumpsys gfxinfo com.android.systemui', then only
// dump the summary information
- int flags = (args == null || args.length == 0) ? FLAG_DUMP_ALL : 0;
+ if (args == null || args.length == 0) {
+ return FLAG_DUMP_ALL;
+ }
+ int flags = 0;
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "framestats":
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3fc7ee3..1e2895d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8322,7 +8322,7 @@
if (mTranslator != null) {
mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
}
- if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) {
+ if (insetsState != null && insetsState.getSourceOrDefaultVisibility(ITYPE_IME)) {
ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsChanged",
getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
}
@@ -8347,7 +8347,7 @@
mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
mTranslator.translateSourceControlsInScreenToAppWindow(activeControls);
}
- if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) {
+ if (insetsState != null && insetsState.getSourceOrDefaultVisibility(ITYPE_IME)) {
ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsControlChanged",
getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 3b4fcc0..f69bb6a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -129,17 +129,15 @@
/**
* The interface that apps use to talk to the window manager.
- * </p><p>
- * Each window manager instance is bound to a particular {@link Display}.
- * To obtain a {@link WindowManager} for a different display, use
- * {@link Context#createDisplayContext} to obtain a {@link Context} for that
- * display, then use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code>
- * to get the WindowManager.
- * </p><p>
- * The simplest way to show a window on another display is to create a
- * {@link Presentation}. The presentation will automatically obtain a
- * {@link WindowManager} and {@link Context} for that display.
- * </p>
+ * <p>
+ * Each window manager instance is bound to a {@link Display}. To obtain the
+ * <code>WindowManager</code> associated with a display,
+ * call {@link Context#createWindowContext(Display, int, Bundle)} to get the display's UI context,
+ * then call {@link Context#getSystemService(String)} or {@link Context#getSystemService(Class)} on
+ * the UI context.
+ * <p>
+ * The simplest way to show a window on a particular display is to create a {@link Presentation},
+ * which automatically obtains a <code>WindowManager</code> and context for the display.
*/
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
@@ -4792,6 +4790,16 @@
return Integer.toString(inputFeature);
}
}
+
+ /**
+ * True if the window should consume all pointer events itself, regardless of whether they
+ * are inside of the window. If the window is modal, its touchable region will expand to the
+ * size of its task.
+ * @hide
+ */
+ public boolean isModal() {
+ return (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+ }
}
/**
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index e634d60..94f6333 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -236,6 +236,9 @@
*/
int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
+ // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
+ // make app pair split only have single root then we can just attach the
+ // divider to the single root task in shell.
int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3;
int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100;
int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101;
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index bc21488..dc4c59a 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -115,13 +115,13 @@
from a window, mapping from windowId -> timestamp. */
private static final SparseLongArray sScrollingWindows = new SparseLongArray();
- private static AccessibilityCache sAccessibilityCache;
+ private static SparseArray<AccessibilityCache> sCaches = new SparseArray<>();
private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
private final Object mInstanceLock = new Object();
- private final AccessibilityManager mAccessibilityManager;
+ private final AccessibilityManager mAccessibilityManager;
private volatile int mInteractionId = -1;
private volatile int mCallingUid = Process.INVALID_UID;
@@ -150,7 +150,37 @@
@UnsupportedAppUsage()
public static AccessibilityInteractionClient getInstance() {
final long threadId = Thread.currentThread().getId();
- return getInstanceForThread(threadId, true);
+ return getInstanceForThread(threadId);
+ }
+
+ /**
+ * <strong>Note:</strong> We keep one instance per interrogating thread since
+ * the instance contains state which can lead to undesired thread interleavings.
+ * We do not have a thread local variable since other threads should be able to
+ * look up the correct client knowing a thread id. See ViewRootImpl for details.
+ *
+ * @return The client for a given <code>threadId</code>.
+ */
+ public static AccessibilityInteractionClient getInstanceForThread(long threadId) {
+ synchronized (sStaticLock) {
+ AccessibilityInteractionClient client = sClients.get(threadId);
+ if (client == null) {
+ client = new AccessibilityInteractionClient();
+ sClients.put(threadId, client);
+ }
+ return client;
+ }
+ }
+
+ /**
+ * @return The client for the current thread.
+ */
+ public static AccessibilityInteractionClient getInstance(Context context) {
+ final long threadId = Thread.currentThread().getId();
+ if (context != null) {
+ return getInstanceForThread(threadId, context);
+ }
+ return getInstanceForThread(threadId);
}
/**
@@ -162,61 +192,11 @@
* @return The client for a given <code>threadId</code>.
*/
public static AccessibilityInteractionClient getInstanceForThread(long threadId,
- boolean initializeCache) {
- synchronized (sStaticLock) {
- AccessibilityInteractionClient client = sClients.get(threadId);
- if (client == null) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- // Don't initialize a cache for the system process
- client = new AccessibilityInteractionClient(false);
- } else {
- client = new AccessibilityInteractionClient(initializeCache);
- }
- sClients.put(threadId, client);
- }
- return client;
- }
- }
-
- /**
- * @return The client for the current thread.
- */
- public static AccessibilityInteractionClient getInstance(Context context) {
- return getInstance(/* initializeCache= */true, context);
- }
-
- /**
- * @param initializeCache whether to initialize the cache in a new client instance
- * @return The client for the current thread.
- */
- public static AccessibilityInteractionClient getInstance(boolean initializeCache,
Context context) {
- final long threadId = Thread.currentThread().getId();
- if (context != null) {
- return getInstanceForThread(threadId, initializeCache, context);
- }
- return getInstanceForThread(threadId, initializeCache);
- }
-
- /**
- * <strong>Note:</strong> We keep one instance per interrogating thread since
- * the instance contains state which can lead to undesired thread interleavings.
- * We do not have a thread local variable since other threads should be able to
- * look up the correct client knowing a thread id. See ViewRootImpl for details.
- *
- * @param initializeCache whether to initialize the cache in a new client instance
- * @return The client for a given <code>threadId</code>.
- */
- public static AccessibilityInteractionClient getInstanceForThread(
- long threadId, boolean initializeCache, Context context) {
synchronized (sStaticLock) {
AccessibilityInteractionClient client = sClients.get(threadId);
if (client == null) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- client = new AccessibilityInteractionClient(false, context);
- } else {
- client = new AccessibilityInteractionClient(initializeCache, context);
- }
+ client = new AccessibilityInteractionClient(context);
sClients.put(threadId, client);
}
return client;
@@ -238,12 +218,30 @@
/**
* Adds a cached accessibility service connection.
*
+ * Adds a cache if {@code initializeCache} is true
* @param connectionId The connection id.
* @param connection The connection.
+ * @param initializeCache whether to initialize a cache
*/
- public static void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
+ public static void addConnection(int connectionId, IAccessibilityServiceConnection connection,
+ boolean initializeCache) {
synchronized (sConnectionCache) {
sConnectionCache.put(connectionId, connection);
+ if (!initializeCache) {
+ return;
+ }
+ sCaches.put(connectionId, new AccessibilityCache(
+ new AccessibilityCache.AccessibilityNodeRefresher()));
+ }
+ }
+
+ /**
+ * Gets a cached associated with the connection id if available.
+ *
+ */
+ public static AccessibilityCache getCache(int connectionId) {
+ synchronized (sConnectionCache) {
+ return sCaches.get(connectionId);
}
}
@@ -255,6 +253,7 @@
public static void removeConnection(int connectionId) {
synchronized (sConnectionCache) {
sConnectionCache.remove(connectionId);
+ sCaches.remove(connectionId);
}
}
@@ -263,32 +262,21 @@
* tests need to be able to verify this class's interactions with the cache
*/
@VisibleForTesting
- public static void setCache(AccessibilityCache cache) {
- sAccessibilityCache = cache;
+ public static void setCache(int connectionId, AccessibilityCache cache) {
+ synchronized (sConnectionCache) {
+ sCaches.put(connectionId, cache);
+ }
}
private AccessibilityInteractionClient() {
/* reducing constructor visibility */
- this(true);
- }
-
- private AccessibilityInteractionClient(boolean initializeCache) {
- initializeCache(initializeCache);
mAccessibilityManager = null;
}
- private AccessibilityInteractionClient(boolean initializeCache, Context context) {
- initializeCache(initializeCache);
+ private AccessibilityInteractionClient(Context context) {
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
}
- private static void initializeCache(boolean initialize) {
- if (initialize && sAccessibilityCache == null) {
- sAccessibilityCache = new AccessibilityCache(
- new AccessibilityCache.AccessibilityNodeRefresher());
- }
- }
-
/**
* Sets the message to be processed if the interacted view hierarchy
* and the interacting client are running in the same thread.
@@ -333,7 +321,7 @@
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
+ * {@link AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window.
* @param bypassCache Whether to bypass the cache.
* @return The {@link AccessibilityWindowInfo}.
@@ -344,21 +332,28 @@
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
AccessibilityWindowInfo window;
- if (!bypassCache && sAccessibilityCache != null) {
- window = sAccessibilityCache.getWindow(accessibilityWindowId);
- if (window != null) {
+ AccessibilityCache cache = getCache(connectionId);
+ if (cache != null) {
+ if (!bypassCache) {
+ window = cache.getWindow(accessibilityWindowId);
+ if (window != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Window cache hit");
+ }
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindow cache",
+ "connectionId=" + connectionId + ";accessibilityWindowId="
+ + accessibilityWindowId + ";bypassCache=false");
+ }
+ return window;
+ }
if (DEBUG) {
- Log.i(LOG_TAG, "Window cache hit");
+ Log.i(LOG_TAG, "Window cache miss");
}
- if (shouldTraceClient()) {
- logTraceClient(connection, "getWindow cache",
- "connectionId=" + connectionId + ";accessibilityWindowId="
- + accessibilityWindowId + ";bypassCache=false");
- }
- return window;
}
+ } else {
if (DEBUG) {
- Log.i(LOG_TAG, "Window cache miss");
+ Log.w(LOG_TAG, "Cache is null for connection id: " + connectionId);
}
}
@@ -374,9 +369,9 @@
+ bypassCache);
}
- if (window != null && sAccessibilityCache != null) {
- if (!bypassCache) {
- sAccessibilityCache.addWindow(window);
+ if (window != null) {
+ if (!bypassCache && cache != null) {
+ cache.addWindow(window);
}
return window;
}
@@ -418,8 +413,9 @@
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
SparseArray<List<AccessibilityWindowInfo>> windows;
- if (sAccessibilityCache != null) {
- windows = sAccessibilityCache.getWindowsOnAllDisplays();
+ AccessibilityCache cache = getCache(connectionId);
+ if (cache != null) {
+ windows = cache.getWindowsOnAllDisplays();
if (windows != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
@@ -433,6 +429,10 @@
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache miss");
}
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Cache is null for connection id: " + connectionId);
+ }
}
long populationTimeStamp;
@@ -447,8 +447,8 @@
logTraceClient(connection, "getWindows", "connectionId=" + connectionId);
}
if (windows != null) {
- if (sAccessibilityCache != null) {
- sAccessibilityCache.setWindowsOnAllDisplays(windows, populationTimeStamp);
+ if (cache != null) {
+ cache.setWindowsOnAllDisplays(windows, populationTimeStamp);
}
return windows;
}
@@ -533,28 +533,35 @@
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- if (!bypassCache && sAccessibilityCache != null) {
- AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode(
- accessibilityWindowId, accessibilityNodeId);
- if (cachedInfo != null) {
+ if (!bypassCache) {
+ AccessibilityCache cache = getCache(connectionId);
+ if (cache != null) {
+ AccessibilityNodeInfo cachedInfo = cache.getNode(
+ accessibilityWindowId, accessibilityNodeId);
+ if (cachedInfo != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Node cache hit for "
+ + idToString(accessibilityWindowId, accessibilityNodeId));
+ }
+ if (shouldTraceClient()) {
+ logTraceClient(connection,
+ "findAccessibilityNodeInfoByAccessibilityId cache",
+ "connectionId=" + connectionId + ";accessibilityWindowId="
+ + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";bypassCache="
+ + bypassCache + ";prefetchFlags=" + prefetchFlags
+ + ";arguments=" + arguments);
+ }
+ return cachedInfo;
+ }
if (DEBUG) {
- Log.i(LOG_TAG, "Node cache hit for "
+ Log.i(LOG_TAG, "Node cache miss for "
+ idToString(accessibilityWindowId, accessibilityNodeId));
}
- if (shouldTraceClient()) {
- logTraceClient(connection,
- "findAccessibilityNodeInfoByAccessibilityId cache",
- "connectionId=" + connectionId + ";accessibilityWindowId="
- + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";bypassCache=" + bypassCache
- + ";prefetchFlags=" + prefetchFlags + ";arguments="
- + arguments);
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Cache is null for connection id: " + connectionId);
}
- return cachedInfo;
- }
- if (DEBUG) {
- Log.i(LOG_TAG, "Node cache miss for "
- + idToString(accessibilityWindowId, accessibilityNodeId));
}
} else {
// No need to prefech nodes in bypass cache case.
@@ -758,19 +765,19 @@
}
/**
- * Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the
+ * Finds the {@link AccessibilityNodeInfo} that has the
* specified focus type. The search is performed in the window whose id is specified
* and starts from the node whose accessibility id is specified.
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
+ * {@link AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window. Use
- * {@link android.view.accessibility.AccessibilityWindowInfo#ANY_WINDOW_ID} to query all
+ * {@link AccessibilityWindowInfo#ANY_WINDOW_ID} to query all
* windows
* @param accessibilityNodeId A unique view id or virtual descendant id from
* where to start the search. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+ * {@link AccessibilityNodeInfo#ROOT_NODE_ID}
* to start from the root.
* @param focusType The focus type.
* @return The accessibility focused {@link AccessibilityNodeInfo}.
@@ -781,8 +788,9 @@
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- if (sAccessibilityCache != null) {
- AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getFocus(focusType,
+ AccessibilityCache cache = getCache(connectionId);
+ if (cache != null) {
+ AccessibilityNodeInfo cachedInfo = cache.getFocus(focusType,
accessibilityNodeId, accessibilityWindowId);
if (cachedInfo != null) {
if (DEBUG) {
@@ -796,6 +804,10 @@
Log.i(LOG_TAG, "Focused node cache miss with "
+ idToString(accessibilityWindowId, accessibilityNodeId));
}
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Cache is null for connection id: " + connectionId);
+ }
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
if (shouldTraceClient()) {
@@ -956,16 +968,25 @@
}
/**
- * Clears the accessibility cache.
+ * Clears the cache associated with {@code connectionId}
+ * @param connectionId the connection id
+ * TODO(207417185): Modify UnsupportedAppUsage
*/
@UnsupportedAppUsage()
- public void clearCache() {
- if (sAccessibilityCache != null) {
- sAccessibilityCache.clear();
+ public void clearCache(int connectionId) {
+ AccessibilityCache cache = getCache(connectionId);
+ if (cache == null) {
+ return;
}
+ cache.clear();
}
- public void onAccessibilityEvent(AccessibilityEvent event) {
+ /**
+ * Informs the cache associated with {@code connectionId} of {@code event}
+ * @param event the event
+ * @param connectionId the connection id
+ */
+ public void onAccessibilityEvent(AccessibilityEvent event, int connectionId) {
switch (event.getEventType()) {
case AccessibilityEvent.TYPE_VIEW_SCROLLED:
updateScrollingWindow(event.getWindowId(), SystemClock.uptimeMillis());
@@ -978,9 +999,14 @@
default:
break;
}
- if (sAccessibilityCache != null) {
- sAccessibilityCache.onAccessibilityEvent(event);
+ AccessibilityCache cache = getCache(connectionId);
+ if (cache == null) {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Cache is null for connection id: " + connectionId);
+ }
+ return;
}
+ cache.onAccessibilityEvent(event);
}
/**
@@ -1216,8 +1242,15 @@
}
}
info.setSealed(true);
- if (!bypassCache && sAccessibilityCache != null) {
- sAccessibilityCache.add(info);
+ if (!bypassCache) {
+ AccessibilityCache cache = getCache(connectionId);
+ if (cache == null) {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Cache is null for connection id: " + connectionId);
+ }
+ return;
+ }
+ cache.add(info);
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index bb3f4e5..dc61727 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -121,6 +121,8 @@
public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED = 0x00000400;
/** @hide */
public static final int STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED = 0x00000800;
+ /** @hide */
+ public static final int STATE_FLAG_AUDIO_DESCRIPTION_BY_DEFAULT_ENABLED = 0x00001000;
/** @hide */
public static final int DALTONIZER_DISABLED = -1;
@@ -244,6 +246,8 @@
@UnsupportedAppUsage(trackingBug = 123768939L)
boolean mIsHighTextContrastEnabled;
+ boolean mIsAudioDescriptionByDefaultRequested;
+
// accessibility tracing state
int mAccessibilityTracingState = 0;
@@ -1293,15 +1297,19 @@
(stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
final boolean highTextContrastEnabled =
(stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
+ final boolean audioDescriptionEnabled =
+ (stateFlags & STATE_FLAG_AUDIO_DESCRIPTION_BY_DEFAULT_ENABLED) != 0;
final boolean wasEnabled = isEnabled();
final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
+
// Ensure listeners get current state from isZzzEnabled() calls.
mIsEnabled = enabled;
mIsTouchExplorationEnabled = touchExplorationEnabled;
mIsHighTextContrastEnabled = highTextContrastEnabled;
+ mIsAudioDescriptionByDefaultRequested = audioDescriptionEnabled;
if (wasEnabled != isEnabled()) {
notifyAccessibilityStateChanged();
@@ -1678,6 +1686,29 @@
}
}
+ /**
+ * Determines if users want to select sound track with audio description by default.
+ *
+ * Audio description, also referred to as a video description, described video, or
+ * more precisely called a visual description, is a form of narration used to provide
+ * information surrounding key visual elements in a media work for the benefit of
+ * blind and visually impaired consumers.
+ *
+ * The method provides the preference value to content provider apps to select the
+ * default sound track during playing a video or movie.
+ *
+ * @return {@code true} if the audio description is enabled, {@code false} otherwise.
+ */
+ public boolean isAudioDescriptionRequested() {
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ return mIsAudioDescriptionByDefaultRequested;
+ }
+ }
+
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 4730eaa..7680aa6 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3957,8 +3957,10 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeString(mCollectionItemInfo.getRowTitle());
parcel.writeInt(mCollectionItemInfo.getRowIndex());
parcel.writeInt(mCollectionItemInfo.getRowSpan());
+ parcel.writeString(mCollectionItemInfo.getColumnTitle());
parcel.writeInt(mCollectionItemInfo.getColumnIndex());
parcel.writeInt(mCollectionItemInfo.getColumnSpan());
parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0);
@@ -4100,8 +4102,9 @@
ci.mHierarchical, ci.mSelectionMode);
CollectionItemInfo cii = other.mCollectionItemInfo;
mCollectionItemInfo = (cii == null) ? null
- : new CollectionItemInfo(cii.mRowIndex, cii.mRowSpan, cii.mColumnIndex,
- cii.mColumnSpan, cii.mHeading, cii.mSelected);
+ : new CollectionItemInfo(cii.mRowTitle, cii.mRowIndex, cii.mRowSpan,
+ cii.mColumnTitle, cii.mColumnIndex, cii.mColumnSpan,
+ cii.mHeading, cii.mSelected);
ExtraRenderingInfo ti = other.mExtraRenderingInfo;
mExtraRenderingInfo = (ti == null) ? null
: new ExtraRenderingInfo(ti);
@@ -4221,8 +4224,10 @@
if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
mCollectionItemInfo = isBitSet(nonDefaultFields, fieldIndex++)
? CollectionItemInfo.obtain(
+ parcel.readString(),
parcel.readInt(),
parcel.readInt(),
+ parcel.readString(),
parcel.readInt(),
parcel.readInt(),
parcel.readInt() == 1,
@@ -5570,8 +5575,9 @@
* @hide
*/
public static CollectionItemInfo obtain(CollectionItemInfo other) {
- return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, other.mColumnIndex,
- other.mColumnSpan, other.mHeading, other.mSelected);
+ return CollectionItemInfo.obtain(other.mRowTitle, other.mRowIndex, other.mRowSpan,
+ other.mColumnTitle, other.mColumnIndex, other.mColumnSpan, other.mHeading,
+ other.mSelected);
}
/**
@@ -5612,10 +5618,36 @@
*/
public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
int columnIndex, int columnSpan, boolean heading, boolean selected) {
+ return obtain(null, rowIndex, rowSpan, null, columnIndex,
+ columnSpan, heading, selected);
+ }
+
+ /**
+ * Obtains a pooled instance.
+ *
+ * <p>In most situations object pooling is not beneficial. Creates a new instance using the
+ * constructor {@link
+ * AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
+ * int, int, int, boolean, boolean)} instead.
+ *
+ * @param rowTitle The row title at which the item is located.
+ * @param rowIndex The row index at which the item is located.
+ * @param rowSpan The number of rows the item spans.
+ * @param columnTitle The column title at which the item is located.
+ * @param columnIndex The column index at which the item is located.
+ * @param columnSpan The number of columns the item spans.
+ * @param heading Whether the item is a heading. (Prefer
+ * {@link AccessibilityNodeInfo#setHeading(boolean)})
+ * @param selected Whether the item is selected.
+ */
+ @NonNull
+ public static CollectionItemInfo obtain(@Nullable String rowTitle, int rowIndex,
+ int rowSpan, @Nullable String columnTitle, int columnIndex, int columnSpan,
+ boolean heading, boolean selected) {
final CollectionItemInfo info = sPool.acquire();
if (info == null) {
- return new CollectionItemInfo(
- rowIndex, rowSpan, columnIndex, columnSpan, heading, selected);
+ return new CollectionItemInfo(rowTitle, rowIndex, rowSpan, columnTitle,
+ columnIndex, columnSpan, heading, selected);
}
info.mRowIndex = rowIndex;
@@ -5624,6 +5656,8 @@
info.mColumnSpan = columnSpan;
info.mHeading = heading;
info.mSelected = selected;
+ info.mRowTitle = rowTitle;
+ info.mColumnTitle = columnTitle;
return info;
}
@@ -5633,6 +5667,8 @@
private int mColumnSpan;
private int mRowSpan;
private boolean mSelected;
+ private String mRowTitle;
+ private String mColumnTitle;
/**
* Creates a new instance.
@@ -5660,12 +5696,33 @@
*/
public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan,
boolean heading, boolean selected) {
+ this(null, rowIndex, rowSpan, null, columnIndex, columnSpan,
+ heading, selected);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param rowTitle The row title at which the item is located.
+ * @param rowIndex The row index at which the item is located.
+ * @param rowSpan The number of rows the item spans.
+ * @param columnTitle The column title at which the item is located.
+ * @param columnIndex The column index at which the item is located.
+ * @param columnSpan The number of columns the item spans.
+ * @param heading Whether the item is a heading.
+ * @param selected Whether the item is selected.
+ */
+ public CollectionItemInfo(@Nullable String rowTitle, int rowIndex, int rowSpan,
+ @Nullable String columnTitle, int columnIndex, int columnSpan, boolean heading,
+ boolean selected) {
mRowIndex = rowIndex;
mRowSpan = rowSpan;
mColumnIndex = columnIndex;
mColumnSpan = columnSpan;
mHeading = heading;
mSelected = selected;
+ mRowTitle = rowTitle;
+ mColumnTitle = columnTitle;
}
/**
@@ -5725,6 +5782,26 @@
}
/**
+ * Gets the row title at which the item is located.
+ *
+ * @return The row title.
+ */
+ @Nullable
+ public String getRowTitle() {
+ return mRowTitle;
+ }
+
+ /**
+ * Gets the column title at which the item is located.
+ *
+ * @return The column title.
+ */
+ @Nullable
+ public String getColumnTitle() {
+ return mColumnTitle;
+ }
+
+ /**
* Recycles this instance.
*
* <p>In most situations object pooling is not beneficial, and recycling is not necessary.
@@ -5741,6 +5818,8 @@
mRowSpan = 0;
mHeading = false;
mSelected = false;
+ mRowTitle = null;
+ mColumnTitle = null;
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 078ab25..4e8d2da 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -98,4 +98,6 @@
int getFocusStrokeWidth();
int getFocusColor();
+
+ boolean isAudioDescriptionByDefaultEnabled();
}
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index ddf68fc..67d9667 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -38,9 +38,14 @@
* or {@link Float#NaN} to leave unchanged.
* @param centerY the screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
+ * @param magnificationFrameOffsetRatioX Indicate the X coordinate offset between
+ * frame position X and centerX
+ * @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset between
+ * frame position Y and centerY
* @param callback The callback called when the animation is completed or interrupted.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
in IRemoteMagnificationAnimationCallback callback);
/**
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 8514f6f..bcab366 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -483,6 +483,8 @@
/**
* Returns the component name of the system service that is consuming the captured events for
* the current user.
+ *
+ * @throws RuntimeException if getting the component name is timed out.
*/
@Nullable
public ComponentName getServiceComponentName() {
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 35e60b8..74ca913 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -44,6 +44,7 @@
import android.view.autofill.AutofillId;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -585,6 +586,16 @@
}
/**
+ * An internal variant of {@link #setInitialSurroundingText(CharSequence)}.
+ *
+ * @param surroundingText {@link SurroundingText} to be set.
+ * @hide
+ */
+ public final void setInitialSurroundingTextInternal(@NonNull SurroundingText surroundingText) {
+ mInitialSurroundingText = surroundingText;
+ }
+
+ /**
* Editors may use this method to provide initial input text to IMEs. As the surrounding text
* could be used to provide various input assistance, we recommend editors to provide the
* complete initial input text in its {@link View#onCreateInputConnection(EditorInfo)} callback.
@@ -972,6 +983,35 @@
}
/**
+ * @return A deep copy of {@link EditorInfo}.
+ * @hide
+ */
+ @NonNull
+ public final EditorInfo createCopyInternal() {
+ final EditorInfo newEditorInfo = new EditorInfo();
+ newEditorInfo.inputType = inputType;
+ newEditorInfo.imeOptions = imeOptions;
+ newEditorInfo.privateImeOptions = privateImeOptions;
+ newEditorInfo.internalImeOptions = internalImeOptions;
+ newEditorInfo.actionLabel = TextUtils.stringOrSpannedString(actionLabel);
+ newEditorInfo.actionId = actionId;
+ newEditorInfo.initialSelStart = initialSelStart;
+ newEditorInfo.initialSelEnd = initialSelEnd;
+ newEditorInfo.initialCapsMode = initialCapsMode;
+ newEditorInfo.hintText = TextUtils.stringOrSpannedString(hintText);
+ newEditorInfo.label = TextUtils.stringOrSpannedString(label);
+ newEditorInfo.packageName = packageName;
+ newEditorInfo.autofillId = autofillId;
+ newEditorInfo.fieldId = fieldId;
+ newEditorInfo.fieldName = fieldName;
+ newEditorInfo.extras = extras != null ? extras.deepCopy() : null;
+ newEditorInfo.mInitialSurroundingText = mInitialSurroundingText;
+ newEditorInfo.hintLocales = hintLocales;
+ newEditorInfo.contentMimeTypes = ArrayUtils.cloneOrNull(contentMimeTypes);
+ return newEditorInfo;
+ }
+
+ /**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e023ed5..7566cea 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1838,6 +1838,69 @@
}
/**
+ * Sends an async signal to the IME to reset the currently served {@link InputConnection}.
+ *
+ * @param inputConnection the connection to be invalidated.
+ * @param textSnapshot {@link TextSnapshot} to be used to update {@link EditorInfo}.
+ * @param sessionId the session ID to be sent.
+ * @hide
+ */
+ public void doInvalidateInput(@NonNull RemoteInputConnectionImpl inputConnection,
+ @NonNull TextSnapshot textSnapshot, int sessionId) {
+ synchronized (mH) {
+ if (mServedInputConnection != inputConnection || mCurrentTextBoxAttribute == null) {
+ // OK to ignore because the calling InputConnection is already abandoned.
+ return;
+ }
+ final EditorInfo editorInfo = mCurrentTextBoxAttribute.createCopyInternal();
+ editorInfo.initialSelStart = mCursorSelStart = textSnapshot.getSelectionStart();
+ editorInfo.initialSelEnd = mCursorSelEnd = textSnapshot.getSelectionEnd();
+ mCursorCandStart = textSnapshot.getCompositionStart();
+ mCursorCandEnd = textSnapshot.getCompositionEnd();
+ editorInfo.initialCapsMode = textSnapshot.getCursorCapsMode();
+ editorInfo.setInitialSurroundingTextInternal(textSnapshot.getSurroundingText());
+ mCurrentInputMethodSession.invalidateInput(editorInfo, mServedInputConnection,
+ sessionId);
+ }
+ }
+
+ /**
+ * Gives a hint to the system that the text associated with {@code view} is updated by something
+ * that is not an input method editor (IME), so that the system can cancel any pending text
+ * editing requests from the IME until it receives the new editing context such as surrounding
+ * text provided by {@link InputConnection#takeSnapshot()}.
+ *
+ * <p>When {@code view} does not support {@link InputConnection#takeSnapshot()} protocol,
+ * calling this method may trigger {@link View#onCreateInputConnection(EditorInfo)}.</p>
+ *
+ * <p>Unlike {@link #restartInput(View)}, this API does not immediately interact with
+ * {@link InputConnection}. Instead, the application may later receive
+ * {@link InputConnection#takeSnapshot()} as needed so that the system can capture new editing
+ * context for the IME. For instance, successive invocations of this API can be coerced into a
+ * single (or zero) callback of {@link InputConnection#takeSnapshot()}.</p>
+ *
+ * @param view The view whose text has changed.
+ * @see #restartInput(View)
+ */
+ public void invalidateInput(@NonNull View view) {
+ Objects.requireNonNull(view);
+
+ // Re-dispatch if there is a context mismatch.
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.invalidateInput(view);
+ return;
+ }
+
+ synchronized (mH) {
+ if (mServedInputConnection == null || getServedViewLocked() != view) {
+ return;
+ }
+ mServedInputConnection.scheduleInvalidateInput();
+ }
+ }
+
+ /**
* Called when {@link DelegateImpl#startInput}, {@link #restartInput(View)},
* {@link #MSG_BIND} or {@link #MSG_UNBIND}.
* Note that this method should *NOT* be called inside of {@code mH} lock to prevent start input
@@ -1929,7 +1992,7 @@
}
// Hook 'em up and let 'er rip.
- mCurrentTextBoxAttribute = tba;
+ mCurrentTextBoxAttribute = tba.createCopyInternal();
mServedConnecting = false;
if (mServedInputConnection != null) {
@@ -2202,6 +2265,10 @@
return;
}
+ if (mServedInputConnection != null && mServedInputConnection.hasPendingInvalidation()) {
+ return;
+ }
+
if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
|| mCursorCandStart != candidatesStart
|| mCursorCandEnd != candidatesEnd) {
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
index 52c1cd4..a178ee8 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -18,11 +18,12 @@
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
-import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import com.android.internal.view.IInputContext;
+
/**
* The InputMethodSession interface provides the per-client functionality
* of {@link InputMethod} that is safe to expose to applications.
@@ -175,8 +176,8 @@
* 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY},
* {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set.
*
- * @deprecated Starting in {@link Build.VERSION_CODES#S} the system no longer invokes this
- * method, instead it explicitly shows or hides the IME. An {@code InputMethodService}
+ * @deprecated Starting in {@link android.os.Build.VERSION_CODES#S} the system no longer invokes
+ * this method, instead it explicitly shows or hides the IME. An {@code InputMethodService}
* wishing to toggle its own visibility should instead invoke {@link
* InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf}
*/
@@ -205,4 +206,20 @@
* @hide
*/
public void removeImeSurface();
+
+ /**
+ * Called when {@code inputContext} is about to be reset with {@code sessionId}.
+ *
+ * <p>The actual implementation should ignore if {@code inputContext} is no longer the current
+ * {@link InputConnection} due to a stale callback.</p>
+ *
+ * @param editorInfo {@link EditorInfo} to be used
+ * @param inputContext specifies which {@link InputConnection} is being updated.
+ * @param sessionId the ID to be specified to
+ * {@link com.android.internal.inputmethod.InputConnectionCommandHeader}.
+ * @hide
+ */
+ default void invalidateInputInternal(EditorInfo editorInfo, IInputContext inputContext,
+ int sessionId) {
+ }
}
diff --git a/core/java/android/view/inputmethod/InputMethodSessionWrapper.java b/core/java/android/view/inputmethod/InputMethodSessionWrapper.java
index ef1814b..a199520 100644
--- a/core/java/android/view/inputmethod/InputMethodSessionWrapper.java
+++ b/core/java/android/view/inputmethod/InputMethodSessionWrapper.java
@@ -24,6 +24,7 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodSession;
/**
@@ -142,6 +143,15 @@
}
}
+ @AnyThread
+ void invalidateInput(EditorInfo editorInfo, IInputContext inputContext, int sessionId) {
+ try {
+ mSession.invalidateInput(editorInfo, inputContext, sessionId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
/**
* @return {@link IInputMethodSession#toString()} as a debug string.
*/
diff --git a/core/java/android/view/translation/TranslationContext.java b/core/java/android/view/translation/TranslationContext.java
index 210fe32..d79ff0b 100644
--- a/core/java/android/view/translation/TranslationContext.java
+++ b/core/java/android/view/translation/TranslationContext.java
@@ -17,6 +17,10 @@
package android.view.translation;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.assist.ActivityId;
+import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
@@ -65,6 +69,46 @@
return 0;
}
+ /**
+ * The identifier for the Activity which needs UI translation.
+ *
+ * @hide
+ */
+ @Nullable
+ private final ActivityId mActivityId;
+
+ private static ActivityId defaultActivityId() {
+ return null;
+ }
+
+ private void parcelActivityId(@NonNull Parcel dest, int flags) {
+ dest.writeBoolean(mActivityId != null);
+ if (mActivityId != null) {
+ mActivityId.writeToParcel(dest, flags);
+ }
+ }
+
+ @Nullable
+ private ActivityId unparcelActivityId(@NonNull Parcel in) {
+ final boolean hasActivityId = in.readBoolean();
+ return hasActivityId ? new ActivityId(in) : null;
+ }
+
+ /**
+ * Returns the identifier for the Activity which needs UI translation or {@code null}
+ * if it is a non-UI translation request.
+ *
+ * NOTE: If the application receiving this ActivityId also provides a ContentCaptureService, it
+ * can be used to associate a TranslationRequest with a particular ContentCaptureSession.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public ActivityId getActivityId() {
+ return mActivityId;
+ }
+
@DataClass.Suppress({"setSourceSpec", "setTargetSpec"})
abstract static class BaseBuilder {
@@ -119,7 +163,8 @@
/* package-private */ TranslationContext(
@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec targetSpec,
- @TranslationFlag int translationFlags) {
+ @TranslationFlag int translationFlags,
+ @Nullable ActivityId activityId) {
this.mSourceSpec = sourceSpec;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSourceSpec);
@@ -133,6 +178,7 @@
FLAG_LOW_LATENCY
| FLAG_TRANSLITERATION
| FLAG_DEFINITIONS);
+ this.mActivityId = activityId;
// onConstructed(); // You can define this method to get a callback
}
@@ -170,19 +216,24 @@
return "TranslationContext { " +
"sourceSpec = " + mSourceSpec + ", " +
"targetSpec = " + mTargetSpec + ", " +
- "translationFlags = " + translationFlagToString(mTranslationFlags) +
+ "translationFlags = " + translationFlagToString(mTranslationFlags) + ", " +
+ "activityId = " + mActivityId +
" }";
}
@Override
@DataClass.Generated.Member
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ byte flg = 0;
+ if (mActivityId != null) flg |= 0x8;
+ dest.writeByte(flg);
dest.writeTypedObject(mSourceSpec, flags);
dest.writeTypedObject(mTargetSpec, flags);
dest.writeInt(mTranslationFlags);
+ parcelActivityId(dest, flags);
}
@Override
@@ -192,13 +243,15 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ TranslationContext(@NonNull android.os.Parcel in) {
+ /* package-private */ TranslationContext(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
TranslationSpec sourceSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR);
TranslationSpec targetSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR);
int translationFlags = in.readInt();
+ ActivityId activityId = unparcelActivityId(in);
this.mSourceSpec = sourceSpec;
com.android.internal.util.AnnotationValidations.validate(
@@ -213,6 +266,7 @@
FLAG_LOW_LATENCY
| FLAG_TRANSLITERATION
| FLAG_DEFINITIONS);
+ this.mActivityId = activityId;
// onConstructed(); // You can define this method to get a callback
}
@@ -226,7 +280,7 @@
}
@Override
- public TranslationContext createFromParcel(@NonNull android.os.Parcel in) {
+ public TranslationContext createFromParcel(@NonNull Parcel in) {
return new TranslationContext(in);
}
};
@@ -241,6 +295,7 @@
private @NonNull TranslationSpec mSourceSpec;
private @NonNull TranslationSpec mTargetSpec;
private @TranslationFlag int mTranslationFlags;
+ private @Nullable ActivityId mActivityId;
private long mBuilderFieldsSet = 0L;
@@ -274,23 +329,40 @@
return this;
}
+ /**
+ * The identifier for the Activity which needs UI translation.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setActivityId(@NonNull ActivityId value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mActivityId = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull TranslationContext build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x8; // Mark builder used
+ mBuilderFieldsSet |= 0x10; // Mark builder used
if ((mBuilderFieldsSet & 0x4) == 0) {
mTranslationFlags = defaultTranslationFlags();
}
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mActivityId = defaultActivityId();
+ }
TranslationContext o = new TranslationContext(
mSourceSpec,
mTargetSpec,
- mTranslationFlags);
+ mTranslationFlags,
+ mActivityId);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x8) != 0) {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -298,10 +370,10 @@
}
@DataClass.Generated(
- time = 1621545292157L,
+ time = 1638348645427L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/TranslationContext.java",
- inputSignatures = "public static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_LOW_LATENCY\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_TRANSLITERATION\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_DEFINITIONS\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mTranslationFlags\nprivate static int defaultTranslationFlags()\nclass TranslationContext extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_LOW_LATENCY\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_TRANSLITERATION\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_DEFINITIONS\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mTranslationFlags\nprivate final @android.annotation.Nullable android.app.assist.ActivityId mActivityId\nprivate static int defaultTranslationFlags()\nprivate static android.app.assist.ActivityId defaultActivityId()\nprivate void parcelActivityId(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.app.assist.ActivityId unparcelActivityId(android.os.Parcel)\npublic @android.annotation.SystemApi @android.annotation.Nullable android.app.assist.ActivityId getActivityId()\nclass TranslationContext extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 69b4187..357dbc0 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -663,8 +663,13 @@
Log.e(TAG, "Can not find TranslationManager when trying to create translator.");
return null;
}
- final TranslationContext translationContext = new TranslationContext(sourceSpec,
- targetSpec, /* translationFlags= */ 0);
+ final TranslationContext translationContext =
+ new TranslationContext.Builder(sourceSpec, targetSpec)
+ .setActivityId(
+ new ActivityId(
+ mActivity.getTaskId(),
+ mActivity.getShareableActivityToken()))
+ .build();
final Translator translator = tm.createTranslator(translationContext);
if (translator != null) {
final Pair<TranslationSpec, TranslationSpec> specs = new Pair<>(sourceSpec, targetSpec);
diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java
index 827d033..46f80f3 100644
--- a/core/java/android/widget/RemoteViewsListAdapter.java
+++ b/core/java/android/widget/RemoteViewsListAdapter.java
@@ -89,8 +89,7 @@
RemoteViews rv = mRemoteViewsList.get(position);
rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
View v;
- if (convertView != null && rv != null &&
- convertView.getId() == rv.getLayoutId()) {
+ if (convertView != null && convertView.getId() == rv.getLayoutId()) {
v = convertView;
rv.reapply(mContext, v, null /* handler */, null /* size */, mColorResources);
} else {
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
new file mode 100644
index 0000000..c3ef881
--- /dev/null
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -0,0 +1,93 @@
+/*
+ * 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 android.window;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Abstract class to control the policies of the windows that can be displayed on the virtual
+ * display.
+ *
+ * @hide
+ */
+public abstract class DisplayWindowPolicyController {
+ /**
+ * The window flags that we are interested in.
+ * @see android.view.WindowManager.LayoutParams
+ * @see #keepActivityOnWindowFlagsChanged
+ */
+ private int mWindowFlags;
+
+ /**
+ * The system window flags that we are interested in.
+ * @see android.view.WindowManager.LayoutParams
+ * @see #keepActivityOnWindowFlagsChanged
+ */
+ private int mSystemWindowFlags;
+
+ /**
+ * Returns {@code true} if the given window flags contain the flags that we're interested in.
+ */
+ public final boolean isInterestedWindowFlags(int windowFlags, int systemWindowFlags) {
+ return (mWindowFlags & windowFlags) != 0 || (mSystemWindowFlags & systemWindowFlags) != 0;
+ }
+
+ /**
+ * Sets the window flags that we’re interested in and expected
+ * #keepActivityOnWindowFlagsChanged to be called if any changes.
+ */
+ public final void setInterestedWindowFlags(int windowFlags, int systemWindowFlags) {
+ mWindowFlags = windowFlags;
+ mSystemWindowFlags = systemWindowFlags;
+ }
+
+ /**
+ * Returns {@code true} if the given activities can be displayed on this virtual display.
+ */
+ public abstract boolean canContainActivities(@NonNull List<ActivityInfo> activities);
+
+ /**
+ * Called when an Activity window is layouted with the new changes where contains the
+ * window flags that we’re interested in.
+ * Returns {@code false} if the Activity cannot remain on the display and the activity task will
+ * be moved back to default display.
+ */
+ public abstract boolean keepActivityOnWindowFlagsChanged(
+ ActivityInfo activityInfo, int windowFlags, int systemWindowFlags);
+
+ /**
+ * This is called when the top activity of the display is changed.
+ */
+ public void onTopActivityChanged(ComponentName topActivity, int uid) {}
+
+ /**
+ * This is called when the apps that contains running activities on the display has changed.
+ */
+ public void onRunningAppsChanged(int[] runningUids) {}
+
+ /** Dump debug data */
+ public void dump(String prefix, final PrintWriter pw) {
+ pw.println(prefix + "DisplayWindowPolicyController{" + super.toString() + "}");
+ pw.println(prefix + " mWindowFlags=" + mWindowFlags);
+ pw.println(prefix + " mSystemWindowFlags=" + mSystemWindowFlags);
+ }
+}
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 5eb432e..cdfa206 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -19,12 +19,11 @@
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
/** @hide */
oneway interface ITaskFragmentOrganizer {
- void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index b9bf009..090dbff 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -43,6 +43,11 @@
*/
public interface SplashScreen {
/**
+ * The splash screen style is not defined.
+ * @hide
+ */
+ int SPLASH_SCREEN_STYLE_UNDEFINED = -1;
+ /**
* Force splash screen to be empty.
* @hide
*/
@@ -55,6 +60,7 @@
/** @hide */
@IntDef(prefix = { "SPLASH_SCREEN_STYLE_" }, value = {
+ SPLASH_SCREEN_STYLE_UNDEFINED,
SPLASH_SCREEN_STYLE_EMPTY,
SPLASH_SCREEN_STYLE_ICON
})
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java
deleted file mode 100644
index 89d9a95..0000000
--- a/core/java/android/window/TaskFragmentAppearedInfo.java
+++ /dev/null
@@ -1,93 +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 android.window;
-
-import android.annotation.NonNull;
-import android.annotation.TestApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.SurfaceControl;
-
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-@TestApi
-public final class TaskFragmentAppearedInfo implements Parcelable {
-
- @NonNull
- private final TaskFragmentInfo mTaskFragmentInfo;
-
- @NonNull
- private final SurfaceControl mLeash;
-
- /** @hide */
- public TaskFragmentAppearedInfo(
- @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) {
- mTaskFragmentInfo = taskFragmentInfo;
- mLeash = leash;
- }
-
- @NonNull
- public TaskFragmentInfo getTaskFragmentInfo() {
- return mTaskFragmentInfo;
- }
-
- @NonNull
- public SurfaceControl getLeash() {
- return mLeash;
- }
-
- private TaskFragmentAppearedInfo(Parcel in) {
- mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
- mLeash = in.readTypedObject(SurfaceControl.CREATOR);
- }
-
- /** @hide */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeTypedObject(mTaskFragmentInfo, flags);
- dest.writeTypedObject(mLeash, flags);
- }
-
- @NonNull
- public static final Creator<TaskFragmentAppearedInfo> CREATOR =
- new Creator<TaskFragmentAppearedInfo>() {
- @Override
- public TaskFragmentAppearedInfo createFromParcel(Parcel in) {
- return new TaskFragmentAppearedInfo(in);
- }
-
- @Override
- public TaskFragmentAppearedInfo[] newArray(int size) {
- return new TaskFragmentAppearedInfo[size];
- }
- };
-
- @Override
- public String toString() {
- return "TaskFragmentAppearedInfo{"
- + " taskFragmentInfo=" + mTaskFragmentInfo
- + "}";
- }
-
- /** @hide */
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 337c5a1..7e7d370 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -120,8 +120,7 @@
}
/** Called when a TaskFragment is created and organized by this organizer. */
- public void onTaskFragmentAppeared(
- @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {}
/** Called when the status of an organized TaskFragment is changed. */
public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {}
@@ -169,7 +168,7 @@
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
mExecutor.execute(
() -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
}
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 5aa6233..17b675f 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -19,13 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
import android.view.IWindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,7 +35,6 @@
* @hide
*/
public class WindowContextController {
- private final IWindowManager mWms;
/**
* {@code true} to indicate that the {@code mToken} is associated with a
* {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
@@ -56,14 +52,7 @@
* {@link Context#getWindowContextToken()}.
*/
public WindowContextController(@NonNull WindowTokenClient token) {
- this(token, WindowManagerGlobal.getWindowManagerService());
- }
-
- /** Used for test only. DO NOT USE it in production code. */
- @VisibleForTesting
- public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
mToken = token;
- mWms = mockWms;
}
/**
@@ -80,19 +69,7 @@
throw new IllegalStateException("A Window Context can be only attached to "
+ "a DisplayArea once.");
}
- try {
- final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
- displayId, options);
- if (configuration != null) {
- mAttachedToDisplayArea = true;
- // Send the DisplayArea's configuration to WindowContext directly instead of
- // waiting for dispatching from WMS.
- mToken.onConfigurationChanged(configuration, displayId,
- false /* shouldReportConfigChange */);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options);
}
/**
@@ -120,22 +97,14 @@
throw new IllegalStateException("The Window Context should have been attached"
+ " to a DisplayArea.");
}
- try {
- mWms.attachWindowContextToWindowToken(mToken, windowToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mToken.attachToWindowToken(windowToken);
}
/** Detaches the window context from the node it's currently associated with. */
public void detachIfNeeded() {
if (mAttachedToDisplayArea) {
- try {
- mWms.detachWindowContextFromWindowContainer(mToken);
- mAttachedToDisplayArea = false;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mToken.detachFromWindowContainerIfNeeded();
+ mAttachedToDisplayArea = false;
}
}
}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index f3e3859..b331a9e 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -21,6 +21,7 @@
import static android.window.ConfigurationHelper.shouldUpdateResources;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
@@ -31,7 +32,11 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import android.view.IWindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
+import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,10 +64,14 @@
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
+ private IWindowManager mWms;
+
private final Configuration mConfiguration = new Configuration();
private boolean mShouldDumpConfigForIme;
+ private boolean mAttachToWindowContainer;
+
/**
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
@@ -84,6 +93,88 @@
}
/**
+ * Attaches this {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
+ *
+ * @param type The window type of the {@link WindowContext}
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @param options The window context launched option
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayArea(@WindowType int type, int displayId,
+ @Nullable Bundle options) {
+ try {
+ final Configuration configuration = getWindowManagerService()
+ .attachWindowContextToDisplayArea(this, type, displayId, options);
+ if (configuration == null) {
+ return false;
+ }
+ onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mAttachToWindowContainer = true;
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code DisplayContent}.
+ *
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayContent(int displayId) {
+ final IWindowManager wms = getWindowManagerService();
+ // #createSystemUiContext may call this method before WindowManagerService is initialized.
+ if (wms == null) {
+ return false;
+ }
+ try {
+ final Configuration configuration = wms.attachToDisplayContent(this, displayId);
+ if (configuration == null) {
+ return false;
+ }
+ onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mAttachToWindowContainer = true;
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
+ *
+ * @param windowToken the window token to associated with
+ */
+ public void attachToWindowToken(IBinder windowToken) {
+ try {
+ getWindowManagerService().attachWindowContextToWindowToken(this, windowToken);
+ mAttachToWindowContainer = true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Detaches this {@link WindowTokenClient} from associated WindowContainer if there's one. */
+ public void detachFromWindowContainerIfNeeded() {
+ if (!mAttachToWindowContainer) {
+ return;
+ }
+ try {
+ getWindowManagerService().detachWindowContextFromWindowContainer(this);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private IWindowManager getWindowManagerService() {
+ if (mWms == null) {
+ mWms = WindowManagerGlobal.getWindowManagerService();
+ }
+ return mWms;
+ }
+
+ /**
* Called when {@link Configuration} updates from the server side receive.
*
* @param newConfig the updated {@link Configuration}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index fd9ad0d..359c382 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -186,6 +186,17 @@
= "com.android.internal.app.ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP";
/**
+ * Boolean extra added to "unbundled Sharesheet" delegation intents to signal whether the app
+ * prediction service is available. Our query of the service <em>availability</em> depends on
+ * privileges that are only available in the system, even though the service itself would then
+ * be available to the unbundled component. For now, we just include the query result as part of
+ * the handover intent.
+ * TODO: investigate whether the privileged query is necessary to determine the availability.
+ */
+ protected static final String EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE =
+ "com.android.internal.app.ChooserActivity.EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE";
+
+ /**
* Transition name for the first image preview.
* To be used for shared element transition into this activity.
* @hide
@@ -757,6 +768,11 @@
delegationIntent.setComponent(delegateActivity);
delegationIntent.putExtra(Intent.EXTRA_INTENT, getIntent());
delegationIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
+
+ // Query prediction availability; mIsAppPredictorComponentAvailable isn't initialized.
+ delegationIntent.putExtra(
+ EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE, isAppPredictionServiceAvailable());
+
delegationIntent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
// Don't close until the delegate finishes, or the token will be invalidated.
@@ -971,7 +987,8 @@
return false;
}
- // Check if the app prediction component actually exists on the device.
+ // Check if the app prediction component actually exists on the device. The component is
+ // only visible when this is running in a system activity; otherwise this check will fail.
Intent intent = new Intent();
intent.setComponent(appPredictionComponentName);
if (getPackageManager().resolveService(intent, PackageManager.MATCH_ALL) == null) {
diff --git a/core/java/com/android/internal/app/ConfirmUserCreationActivity.java b/core/java/com/android/internal/app/ConfirmUserCreationActivity.java
index 0047f43..ee4d46d 100644
--- a/core/java/com/android/internal/app/ConfirmUserCreationActivity.java
+++ b/core/java/com/android/internal/app/ConfirmUserCreationActivity.java
@@ -23,12 +23,10 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -44,6 +42,8 @@
private static final String TAG = "CreateUser";
+ private static final String USER_TYPE = UserManager.USER_TYPE_FULL_SECONDARY;
+
private String mUserName;
private String mAccountName;
private String mAccountType;
@@ -103,7 +103,7 @@
boolean cantCreateUser = mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)
|| !mUserManager.isAdminUser();
// Check the system state and user count
- boolean cantCreateAnyMoreUsers = !mUserManager.canAddMoreUsers();
+ boolean cantCreateAnyMoreUsers = !mUserManager.canAddMoreUsers(USER_TYPE);
// Check the account existence
final Account account = new Account(mAccountName, mAccountType);
boolean accountExists = mAccountName != null && mAccountType != null
@@ -130,7 +130,7 @@
setResult(RESULT_CANCELED);
if (which == BUTTON_POSITIVE && mCanProceed) {
Log.i(TAG, "Ok, creating user");
- UserInfo user = mUserManager.createUser(mUserName, 0);
+ UserInfo user = mUserManager.createUser(mUserName, USER_TYPE, 0);
if (user == null) {
Log.e(TAG, "Couldn't create user");
finish();
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index bff813e..6a6f60e 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -512,6 +512,11 @@
*/
public static final String DEFAULT_QR_CODE_SCANNER = "default_qr_code_scanner";
+ /**
+ * (boolean) Whether the task manager entrypoint is enabled.
+ */
+ public static final String TASK_MANAGER_ENABLED = "task_manager_enabled";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
index 4dbd941..e6fafe0 100644
--- a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -43,9 +43,11 @@
@NonNull
private final IInputContext mIInputContext;
+ private final int mSessionId;
- private IInputContextInvoker(@NonNull IInputContext inputContext) {
+ private IInputContextInvoker(@NonNull IInputContext inputContext, int sessionId) {
mIInputContext = inputContext;
+ mSessionId = sessionId;
}
/**
@@ -56,13 +58,36 @@
*/
public static IInputContextInvoker create(@NonNull IInputContext inputContext) {
Objects.requireNonNull(inputContext);
- return new IInputContextInvoker(inputContext);
+ return new IInputContextInvoker(inputContext, 0);
+ }
+
+ /**
+ * Creates a new instance of {@link IInputContextInvoker} with the given {@code sessionId}.
+ *
+ * @param sessionId the new session ID to be used.
+ * @return A new instance of {@link IInputContextInvoker}.
+ */
+ @NonNull
+ public IInputContextInvoker cloneWithSessionId(int sessionId) {
+ return new IInputContextInvoker(mIInputContext, sessionId);
+ }
+
+ /**
+ * @param inputContext {@code IInputContext} to be compared with
+ * @return {@code true} if the underlying {@code IInputContext} is the same. {@code false} if
+ * {@code inputContext} is {@code null}.
+ */
+ @AnyThread
+ public boolean isSameConnection(@NonNull IInputContext inputContext) {
+ if (inputContext == null) {
+ return false;
+ }
+ return mIInputContext.asBinder() == inputContext.asBinder();
}
@NonNull
InputConnectionCommandHeader createHeader() {
- // TODO(b/203086369): Propagate session ID for interruption
- return new InputConnectionCommandHeader(0 /* sessionId */);
+ return new InputConnectionCommandHeader(mSessionId);
}
/**
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 5503226..6c2b330 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -43,6 +43,7 @@
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.TextAttribute;
+import android.view.inputmethod.TextSnapshot;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
@@ -50,6 +51,7 @@
import java.lang.annotation.Retention;
import java.lang.ref.WeakReference;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -87,8 +89,8 @@
private final InputMethodManager mParentInputMethodManager;
private final WeakReference<View> mServedView;
- // TODO(b/203086369): This is to be used when interruption is implemented.
private final AtomicInteger mCurrentSessionId = new AtomicInteger(0);
+ private final AtomicBoolean mHasPendingInvalidation = new AtomicBoolean();
public RemoteInputConnectionImpl(@NonNull Looper looper,
@NonNull InputConnection inputConnection,
@@ -111,6 +113,14 @@
}
/**
+ * @return {@code true} if there is a pending {@link InputMethodManager#invalidateInput(View)}
+ * call.
+ */
+ public boolean hasPendingInvalidation() {
+ return mHasPendingInvalidation.get();
+ }
+
+ /**
* @return {@code true} until the target {@link InputConnection} receives
* {@link InputConnection#closeConnection()} as a result of {@link #deactivate()}.
*/
@@ -129,6 +139,56 @@
}
/**
+ * Schedule a task to execute
+ * {@link InputMethodManager#doInvalidateInput(RemoteInputConnectionImpl, TextSnapshot, int)}
+ * on the associated Handler if not yet scheduled.
+ *
+ * <p>By calling {@link InputConnection#takeSnapshot()} directly from the message loop, we can
+ * make sure that application code is not modifying text context in a reentrant manner.</p>
+ */
+ public void scheduleInvalidateInput() {
+ if (mHasPendingInvalidation.compareAndSet(false, true)) {
+ final int nextSessionId = mCurrentSessionId.incrementAndGet();
+ // By calling InputConnection#takeSnapshot() directly from the message loop, we can make
+ // sure that application code is not modifying text context in a reentrant manner.
+ // e.g. We may see methods like EditText#setText() in the callstack here.
+ mH.post(() -> {
+ try {
+ if (isFinished()) {
+ return;
+ }
+ final InputConnection ic = getInputConnection();
+ if (ic == null) {
+ return;
+ }
+
+ // Clean up composing text and batch edit.
+ ic.finishComposingText();
+ // Also clean up batch edit.
+ while (true) {
+ if (!ic.endBatchEdit()) {
+ break;
+ }
+ }
+
+ final TextSnapshot textSnapshot = ic.takeSnapshot();
+ if (textSnapshot == null) {
+ final View view = getServedView();
+ if (view == null) {
+ return;
+ }
+ mParentInputMethodManager.restartInput(view);
+ return;
+ }
+ mParentInputMethodManager.doInvalidateInput(this, textSnapshot, nextSessionId);
+ } finally {
+ mHasPendingInvalidation.set(false);
+ }
+ });
+ }
+ }
+
+ /**
* Called when this object needs to be permanently deactivated.
*
* <p>Multiple invocations will be simply ignored.</p>
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 06d68e0..aba43d8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3522,6 +3522,10 @@
* <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code>
*/
private int writeHistoryTag(HistoryTag tag) {
+ if (tag.string == null) {
+ Slog.wtfStack(TAG, "writeHistoryTag called with null name");
+ }
+
Integer idxObj = mHistoryTagPool.get(tag);
int idx;
if (idxObj != null) {
@@ -12182,7 +12186,7 @@
mHistoryTags = new SparseArray<>(mHistoryTagPool.size());
for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
- mHistoryTags.put(entry.getValue(), entry.getKey());
+ mHistoryTags.put(entry.getValue() & ~TAG_FIRST_OCCURRENCE_FLAG, entry.getKey());
}
}
@@ -16341,9 +16345,6 @@
for (int i=0; i<numTags; i++) {
int idx = in.readInt();
String str = in.readString();
- if (str == null) {
- throw new ParcelFormatException("null history tag string");
- }
int uid = in.readInt();
HistoryTag tag = new HistoryTag();
tag.string = str;
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 92d5a47..6d4b8c5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -724,9 +724,6 @@
DataOutputStream usapOutputStream = null;
ZygoteArguments args = null;
- // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
- blockSigTerm();
-
LocalSocket sessionSocket = null;
if (argBuffer == null) {
// Read arguments from usapPoolSocket instead.
@@ -742,6 +739,10 @@
ZygoteCommandBuffer tmpArgBuffer = null;
try {
sessionSocket = usapPoolSocket.accept();
+ // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
+ // This is safe from a race condition because the pool is only flushed after
+ // the SystemServer changes its internal state to stop using the USAP pool.
+ blockSigTerm();
usapOutputStream =
new DataOutputStream(sessionSocket.getOutputStream());
@@ -759,9 +760,10 @@
unblockSigTerm();
IoUtils.closeQuietly(sessionSocket);
IoUtils.closeQuietly(tmpArgBuffer);
- blockSigTerm();
}
} else {
+ // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
+ blockSigTerm();
try {
args = ZygoteArguments.getInstance(argBuffer);
} catch (Exception ex) {
diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java
index 1ab316d..3147c34 100644
--- a/core/java/com/android/internal/util/Parcelling.java
+++ b/core/java/com/android/internal/util/Parcelling.java
@@ -23,6 +23,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -306,5 +307,33 @@
return string == null ? null : UUID.fromString(string);
}
}
+
+ /**
+ * A {@link Parcelling} for {@link Instant}.
+ *
+ * The minimum value of an instant uses a millisecond offset of about -3.15e19 which is
+ * larger than Long.MIN_VALUE, so we can use Long.MIN_VALUE as a sentinel value to indicate
+ * a null Instant.
+ */
+ class ForInstant implements Parcelling<Instant> {
+
+ @Override
+ public void parcel(Instant item, Parcel dest, int parcelFlags) {
+ dest.writeLong(item == null ? Long.MIN_VALUE : item.getEpochSecond());
+ dest.writeInt(item == null ? Integer.MIN_VALUE : item.getNano());
+ }
+
+ @Override
+ public Instant unparcel(Parcel source) {
+ long epochSecond = source.readLong();
+ int afterNano = source.readInt();
+
+ if (epochSecond == Long.MIN_VALUE) {
+ return null;
+ } else {
+ return Instant.ofEpochSecond(epochSecond, afterNano);
+ }
+ }
+ }
}
}
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index acb4754..b9eb997 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -22,8 +22,11 @@
import android.view.MotionEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
+import com.android.internal.view.IInputContext;
+
/**
* Sub-interface of IInputMethod which is safe to give to client applications.
* {@hide}
@@ -52,4 +55,6 @@
void removeImeSurface();
void finishInput();
+
+ void invalidateInput(in EditorInfo editorInfo, in IInputContext inputContext, int sessionId);
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index ec3dfad..be5dc00 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -56,6 +56,7 @@
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -95,6 +96,9 @@
// property for runtime configuration differentiation in vendor
private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku";
+ private static final ArrayMap<String, ArraySet<String>> EMPTY_PERMISSIONS =
+ new ArrayMap<>();
+
// Group-ids that are given to all packages as read from etc/permissions/*.xml.
int[] mGlobalGids = EmptyArray.INT;
@@ -281,6 +285,11 @@
final ArrayMap<String, ArraySet<String>> mSystemExtPrivAppPermissions = new ArrayMap<>();
final ArrayMap<String, ArraySet<String>> mSystemExtPrivAppDenyPermissions = new ArrayMap<>();
+ final ArrayMap<String, ArrayMap<String, ArraySet<String>>> mApexPrivAppPermissions =
+ new ArrayMap<>();
+ final ArrayMap<String, ArrayMap<String, ArraySet<String>>> mApexPrivAppDenyPermissions =
+ new ArrayMap<>();
+
final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>();
// Allowed associations between applications. If there are any entries
@@ -417,6 +426,18 @@
return mPrivAppDenyPermissions.get(packageName);
}
+ /** Get privapp permission allowlist for an apk-in-apex. */
+ public ArraySet<String> getApexPrivAppPermissions(String module, String packageName) {
+ return mApexPrivAppPermissions.getOrDefault(module, EMPTY_PERMISSIONS)
+ .get(packageName);
+ }
+
+ /** Get privapp permissions denylist for an apk-in-apex. */
+ public ArraySet<String> getApexPrivAppDenyPermissions(String module, String packageName) {
+ return mApexPrivAppDenyPermissions.getOrDefault(module, EMPTY_PERMISSIONS)
+ .get(packageName);
+ }
+
public ArraySet<String> getVendorPrivAppPermissions(String packageName) {
return mVendorPrivAppPermissions.get(packageName);
}
@@ -632,8 +653,8 @@
if (!isSystemProcess()) {
return;
}
- // Read configuration of features and libs from apex module.
- int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES;
+ // Read configuration of features, libs and priv-app permissions from apex module.
+ int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
// TODO: Use a solid way to filter apex module folders?
for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) {
if (f.isFile() || f.getPath().contains("@")) {
@@ -1116,10 +1137,10 @@
} break;
case "privapp-permissions": {
if (allowPrivappPermissions) {
- // privapp permissions from system, vendor, product and system_ext
- // partitions are stored separately. This is to prevent xml files in
- // the vendor partition from granting permissions to priv apps in the
- // system partition and vice versa.
+ // privapp permissions from system, apex, vendor, product and
+ // system_ext partitions are stored separately. This is to
+ // prevent xml files in the vendor partition from granting
+ // permissions to priv apps in the system partition and vice versa.
boolean vendor = permFile.toPath().startsWith(
Environment.getVendorDirectory().toPath() + "/")
|| permFile.toPath().startsWith(
@@ -1128,6 +1149,8 @@
Environment.getProductDirectory().toPath() + "/");
boolean systemExt = permFile.toPath().startsWith(
Environment.getSystemExtDirectory().toPath() + "/");
+ boolean apex = permFile.toPath().startsWith(
+ Environment.getApexDirectory().toPath() + "/");
if (vendor) {
readPrivAppPermissions(parser, mVendorPrivAppPermissions,
mVendorPrivAppDenyPermissions);
@@ -1137,6 +1160,8 @@
} else if (systemExt) {
readPrivAppPermissions(parser, mSystemExtPrivAppPermissions,
mSystemExtPrivAppDenyPermissions);
+ } else if (apex) {
+ readApexPrivAppPermissions(parser, permFile);
} else {
readPrivAppPermissions(parser, mPrivAppPermissions,
mPrivAppDenyPermissions);
@@ -1692,6 +1717,43 @@
}
}
+
+ /**
+ * Returns the module name for a file in the apex module's partition.
+ */
+ private String getApexModuleNameFromFilePath(Path path) {
+ final Path apexDirectoryPath = Environment.getApexDirectory().toPath();
+ if (!path.startsWith(apexDirectoryPath)) {
+ throw new IllegalArgumentException("File " + path + " is not part of an APEX.");
+ }
+ // File must be in <apex_directory>/<module_name>/[extra_paths/]<xml_file>
+ if (path.getNameCount() <= (apexDirectoryPath.getNameCount() + 1)) {
+ throw new IllegalArgumentException("File " + path + " is in the APEX partition,"
+ + " but not inside a module.");
+ }
+ return path.getName(apexDirectoryPath.getNameCount()).toString();
+ }
+
+ private void readApexPrivAppPermissions(XmlPullParser parser, File permFile)
+ throws IOException, XmlPullParserException {
+ final String moduleName = getApexModuleNameFromFilePath(permFile.toPath());
+ final ArrayMap<String, ArraySet<String>> privAppPermissions;
+ if (mApexPrivAppPermissions.containsKey(moduleName)) {
+ privAppPermissions = mApexPrivAppPermissions.get(moduleName);
+ } else {
+ privAppPermissions = new ArrayMap<>();
+ mApexPrivAppPermissions.put(moduleName, privAppPermissions);
+ }
+ final ArrayMap<String, ArraySet<String>> privAppDenyPermissions;
+ if (mApexPrivAppDenyPermissions.containsKey(moduleName)) {
+ privAppDenyPermissions = mApexPrivAppDenyPermissions.get(moduleName);
+ } else {
+ privAppDenyPermissions = new ArrayMap<>();
+ mApexPrivAppDenyPermissions.put(moduleName, privAppDenyPermissions);
+ }
+ readPrivAppPermissions(parser, privAppPermissions, privAppDenyPermissions);
+ }
+
private static boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 6cea8bf..f2bcb02 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -73,6 +73,9 @@
# Although marked "view" this is mostly graphics stuff
per-file android_view_* = file:/graphics/java/android/graphics/OWNERS
+# Verity
+per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com
+
# VINTF
per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS
per-file android_os_VintfRuntimeInfo* = file:platform/system/libvintf:/OWNERS
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 4c2b114..5e0d9b3 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -34,6 +34,7 @@
#include <vector>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <bionic/malloc.h>
#include <debuggerd/client.h>
#include <log/log.h>
@@ -859,7 +860,22 @@
return poolsSizeKb;
}
+static bool halSupportsGpuPrivateMemory() {
+ int productApiLevel =
+ android::base::GetIntProperty("ro.product.first_api_level",
+ android::base::GetIntProperty("ro.build.version.sdk",
+ __ANDROID_API_FUTURE__));
+ int boardApiLevel =
+ android::base::GetIntProperty("ro.board.api_level",
+ android::base::GetIntProperty("ro.board.first_api_level",
+ __ANDROID_API_FUTURE__));
+
+ return std::min(productApiLevel, boardApiLevel) >= __ANDROID_API_S__;
+}
+
static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz) {
+ static bool gpuPrivateMemorySupported = halSupportsGpuPrivateMemory();
+
struct memtrack_proc* p = memtrack_proc_new();
if (p == nullptr) {
LOG(ERROR) << "getGpuPrivateMemoryKb: Failed to create memtrack_proc";
@@ -876,6 +892,12 @@
ssize_t gpuPrivateMem = memtrack_proc_gl_pss(p);
memtrack_proc_destroy(p);
+
+ // Old HAL implementations may return 0 for GPU private memory if not supported
+ if (gpuPrivateMem == 0 && !gpuPrivateMemorySupported) {
+ return -1;
+ }
+
return gpuPrivateMem / 1024;
}
diff --git a/core/jni/android_view_CompositionSamplingListener.cpp b/core/jni/android_view_CompositionSamplingListener.cpp
index 783b0d4..59c01dc 100644
--- a/core/jni/android_view_CompositionSamplingListener.cpp
+++ b/core/jni/android_view_CompositionSamplingListener.cpp
@@ -16,21 +16,19 @@
#define LOG_TAG "CompositionSamplingListener"
-#include "android_util_Binder.h"
-#include "core_jni_helpers.h"
-
-#include <nativehelper/JNIHelp.h>
-
+#include <android/gui/BnRegionSamplingListener.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
-#include <utils/Log.h>
-#include <utils/RefBase.h>
#include <binder/IServiceManager.h>
-
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
#include <ui/Rect.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include "android_util_Binder.h"
+#include "core_jni_helpers.h"
namespace android {
@@ -41,18 +39,18 @@
jmethodID mDispatchOnSampleCollected;
} gListenerClassInfo;
-struct CompositionSamplingListener : public BnRegionSamplingListener {
+struct CompositionSamplingListener : public gui::BnRegionSamplingListener {
CompositionSamplingListener(JNIEnv* env, jobject listener)
: mListener(env->NewWeakGlobalRef(listener)) {}
- void onSampleCollected(float medianLuma) override {
+ binder::Status onSampleCollected(float medianLuma) override {
JNIEnv* env = AndroidRuntime::getJNIEnv();
LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onSampleCollected.");
jobject listener = env->NewGlobalRef(mListener);
if (listener == NULL) {
// Weak reference went out of scope
- return;
+ return binder::Status::ok();
}
env->CallStaticVoidMethod(gListenerClassInfo.mClass,
gListenerClassInfo.mDispatchOnSampleCollected, listener,
@@ -64,6 +62,8 @@
LOGE_EX(env);
env->ExceptionClear();
}
+
+ return binder::Status::ok();
}
protected:
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 1159c8a..a7ec38f 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -458,10 +458,6 @@
skipCallbacks = true;
}
}
-
- if (skipCallbacks) {
- mInputConsumer.sendFinishedSignal(seq, false);
- }
}
}
diff --git a/core/jni/com_android_internal_security_VerityUtils.cpp b/core/jni/com_android_internal_security_VerityUtils.cpp
index 411a392..c5b3d8a 100644
--- a/core/jni/com_android_internal_security_VerityUtils.cpp
+++ b/core/jni/com_android_internal_security_VerityUtils.cpp
@@ -16,25 +16,25 @@
#define LOG_TAG "VerityUtils"
+#include <android-base/unique_fd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <linux/fsverity.h>
+#include <linux/stat.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <utils/Log.h>
-#include "jni.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/fsverity.h>
-#include <linux/stat.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
-
-#include <android-base/unique_fd.h>
+#include <utils/Log.h>
#include <type_traits>
+#include "jni.h"
+
namespace android {
namespace {
@@ -73,18 +73,31 @@
int statxForFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath) {
ScopedUtfChars path(env, filePath);
+ // Call statx and check STATX_ATTR_VERITY.
struct statx out = {};
if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
return -errno;
}
- // Validity check.
- if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) {
- ALOGE("Unexpected, STATX_ATTR_VERITY not supported by kernel");
- return -ENOSYS;
+ if (out.stx_attributes_mask & STATX_ATTR_VERITY) {
+ return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
}
- return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
+ // STATX_ATTR_VERITY is not supported for the file path.
+ // In this case, call ioctl(FS_IOC_GETFLAGS) and check FS_VERITY_FL.
+ ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ if (rfd.get() < 0) {
+ ALOGE("open failed at %s", path.c_str());
+ return -errno;
+ }
+
+ unsigned int flags;
+ if (ioctl(rfd.get(), FS_IOC_GETFLAGS, &flags) < 0) {
+ ALOGE("ioctl(FS_IOC_GETFLAGS) failed at %s", path.c_str());
+ return -errno;
+ }
+
+ return (flags & FS_VERITY_FL) != 0;
}
int measureFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray digest) {
diff --git a/core/proto/android/app/location_time_zone_manager.proto b/core/proto/android/app/location_time_zone_manager.proto
index 891e9fc..5fdcfdf 100644
--- a/core/proto/android/app/location_time_zone_manager.proto
+++ b/core/proto/android/app/location_time_zone_manager.proto
@@ -23,6 +23,19 @@
option java_multiple_files = true;
option java_outer_classname = "LocationTimeZoneManagerProto";
+// A state enum that matches states for LocationTimeZoneProviderController. See that class for
+// details.
+enum ControllerStateEnum {
+ CONTROLLER_STATE_UNKNOWN = 0;
+ CONTROLLER_STATE_PROVIDERS_INITIALIZING = 1;
+ CONTROLLER_STATE_STOPPED = 2;
+ CONTROLLER_STATE_INITIALIZING = 3;
+ CONTROLLER_STATE_UNCERTAIN = 4;
+ CONTROLLER_STATE_CERTAIN = 5;
+ CONTROLLER_STATE_FAILED = 6;
+ CONTROLLER_STATE_DESTROYED = 7;
+}
+
// Represents the state of the LocationTimeZoneManagerService for use in tests.
message LocationTimeZoneManagerServiceStateProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -30,6 +43,7 @@
optional GeolocationTimeZoneSuggestionProto last_suggestion = 1;
repeated TimeZoneProviderStateProto primary_provider_states = 2;
repeated TimeZoneProviderStateProto secondary_provider_states = 3;
+ repeated ControllerStateEnum controller_states = 4;
}
// The state tracked for a LocationTimeZoneProvider.
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 83e26f6..db5d49d 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -601,7 +601,7 @@
// ringer mode.
optional SettingProto mode_ringer = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto apply_ramping_ringer = 147 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 147; // Used to be apply_ramping_ringer
message MultiSim {
option (android.msg_privacy).dest = DEST_EXPLICIT;
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index f8b5b233..73d6a17 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -242,7 +242,9 @@
optional SettingProto when_to_make_wifi_calls = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto apply_ramping_ringer = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 35;
+ // Next tag = 36;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d3ee98a..aada7eb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1840,11 +1840,11 @@
<permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
android:protectionLevel="signature|privileged" />
- <!-- Allows applications to act as network scorers. @hide @SystemApi-->
+ <!-- @deprecated Allows applications to act as network scorers. @hide @SystemApi-->
<permission android:name="android.permission.SCORE_NETWORKS"
android:protectionLevel="signature|privileged" />
- <!-- Allows applications to request network
+ <!-- @deprecated Allows applications to request network
recommendations and scores from the NetworkScoreService.
@SystemApi
<p>Not for use by third-party applications. @hide -->
@@ -2793,6 +2793,11 @@
android:label="@string/permlab_manageProfileAndDeviceOwners"
android:description="@string/permdesc_manageProfileAndDeviceOwners" />
+ <!-- @SystemApi @hide Allows an application to query device policies set by any admin on
+ the device.-->
+ <permission android:name="android.permission.QUERY_ADMIN_POLICY"
+ android:protectionLevel="signature|role" />
+
<!-- @TestApi @hide Allows an application to reset the record of previous system update freeze
periods. -->
<permission android:name="android.permission.CLEAR_FREEZE_PERIOD"
@@ -3015,6 +3020,13 @@
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to create a "self-managed" association.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows a companion app to associate to Wi-Fi.
<p>Only for use by a single pre-approved app.
@hide
@@ -3463,6 +3475,23 @@
<permission android:name="android.permission.UPDATE_FONTS"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to use the AttestationVerificationService.
+ @hide -->
+ <permission android:name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to export a AttestationVerificationService to verify attestations on
+ behalf of AttestationVerificationManager for system-defined attestation profiles.
+ @hide -->
+ <permission android:name="android.permission.VERIFY_ATTESTATION"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by any AttestationVerificationService to ensure that only the system can
+ bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_ATTESTATION_VERIFICATION_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
@@ -5853,6 +5882,10 @@
<permission android:name="android.permission.ADD_TRUSTED_DISPLAY"
android:protectionLevel="signature" />
+ <!-- Allows an application to create always-unlocked displays. @hide -->
+ <permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY"
+ android:protectionLevel="signature"/>
+
<!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|role" />
@@ -5916,11 +5949,10 @@
<permission android:name="android.permission.RENOUNCE_PERMISSIONS"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to read nearby streaming policy. The policy allows the device
- to stream its notifications and apps to nearby devices.
- @hide -->
+ <!-- Allows an application to read nearby streaming policy. The policy controls
+ whether to allow the device to stream its notifications and apps to nearby devices. -->
<permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="normal" />
<!-- @SystemApi Allows the holder to set the source of the data when setting a clip on the
clipboard.
@@ -5963,6 +5995,14 @@
<permission android:name="android.permission.CREATE_VIRTUAL_DEVICE"
android:protectionLevel="internal|role" />
+ <!-- @SystemApi Must be required by a safety source to send an update using the
+ {@link android.safetycenter.SafetyCenterManager}.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE"
+ android:protectionLevel="signature|privileged" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
@@ -6395,6 +6435,10 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.server.compos.IsolatedCompilationJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
<service android:name="com.android.server.PruneInstantAppsJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/core/res/res/layout-watch/preference_list_fragment_material.xml b/core/res/res/layout-watch/preference_list_fragment_material.xml
index 22e66d5..8f2658b 100644
--- a/core/res/res/layout-watch/preference_list_fragment_material.xml
+++ b/core/res/res/layout-watch/preference_list_fragment_material.xml
@@ -43,7 +43,7 @@
android:paddingStart="@dimen/dialog_padding_material"
android:paddingEnd="@dimen/dialog_padding_material"
android:paddingBottom="8dp"
- android:textAppearance="@style/TextAppearance.Material.Title"
+ android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_horizontal|top" />
</com.android.internal.widget.WatchHeaderListView>
</FrameLayout>
diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml
index 2b9f952..304affe 100644
--- a/core/res/res/layout/splash_screen_view.xml
+++ b/core/res/res/layout/splash_screen_view.xml
@@ -36,6 +36,7 @@
android:layout_marginBottom="60dp"
android:padding="0dp"
android:background="@null"
+ android:forceHasOverlappingRendering="false"
android:contentDescription="@string/splash_screen_view_branding_description"/>
</android.window.SplashScreenView>
\ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index c592ef4..c02981e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor pogings om skerm te ontsluit"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor die aantal keer wat \'n verkeerde wagwoorde ingevoer is wanneer die skerm ontsluit word. Sluit die tablet of vee al die data uit as die wagwoord te veel keer verkeerd ingevoer word."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor die aantal verkeerde wagwoorde wat ingetik word wanneer die skerm ontsluit word, en sluit jou Android TV-toestel of vee al jou Android TV-toestel se data uit as te veel verkeerde wagwoorde ingetik word."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor die aantal verkeerde wagwoorde wat ingevoer word wanneer die skerm ontsluit word, en sluit die inligtingvermaakstelsel of vee al die inligtingvermaakstelsel se data uit as te veel verkeerde wagwoorde ingevoer word."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor die aantal keer wat \'n verkeerde wagwoorde ingevoer is wanneer die skerm ontsluit word. Sluit die foon of vee al die data uit as die wagwoord te veel keer verkeerd ingevoer word."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor die aantal verkeerde wagwoorde wat ingevoer word wanneer die skerm ontsluit word, en sluit die tablet of vee al hierdie gebruiker se data uit as te veel verkeerde wagwoorde ingevoer word."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor die aantal verkeerde wagwoorde wat ingetik word wanneer die skerm ontsluit word, en sluit jou Android TV-toestel of vee al hierdie gebruiker se data uit as hulle te veel verkeerde wagwoorde intik."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor die aantal verkeerde wagwoorde wat ingevoer word wanneer die skerm ontsluit word, en sluit die inligtingvermaakstelsel of vee al hierdie profiel se data uit as te veel verkeerde wagwoorde ingevoer word."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor die aantal verkeerde wagwoorde wat ingevoer word wanneer die skerm ontsluit word, en sluit die foon of vee al hierdie gebruiker se data uit as te veel verkeerde wagwoorde ingevoer word."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Om die skermslot te verander"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Verander die skermslot."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Om alle data uit te vee"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Vee die tablet se data uit sonder waarskuwing, deur \'n fabrieksterugstelling uit te voer."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Vee jou Android TV-toestel se data sonder waarskuwing uit deur \'n fabrieksterugstelling uit te voer."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Vee die inligtingvermaakstelsel se data sonder waarskuwing uit deur \'n fabriekterugstelling te doen."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Vee die foon se data uit sonder waarskuwing, deur \'n fabrieksterugstelling uit te voer."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Vee gebruikerdata uit"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Vee profieldata uit"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Vee gebruikerdata uit"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Vee hierdie gebruiker se data in hierdie tablet sonder waarskuwing uit."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Vee hierdie gebruiker se data op hierdie Android TV-toestel sonder waarskuwing uit."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Vee hierdie profiel se data op hierdie inligtingvermaakstelsel sonder waarskuwing uit."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Vee hierdie gebruiker se data in hierdie foon sonder waarskuwing uit."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Stel die toestel se globale instaan"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Stel die toestel se globale instaanbediener wat gebruik moet word terwyl die beleid geaktiveer is. Net die toesteladministrateur kan die globale instaanbediener stel."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 74fcfc6..be4eeb8 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"የማሳያ-ክፈት ሙከራዎችን ክትትል ያድርጉባቸው"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ማሳያውን በምትከፍትበት ጊዜ በስህተት የተተየቡ የይለፍ ቃሎችን ቁጥር ተቆጣጠር፤ እና ጡባዊ ተኮውን ቆልፍ ወይም በጣም ብዙ የተሳሳቱ የይለፍ ቃሎች ከተተየቡ የጡባዊ ተኮን ውሂብ አጥፋ፡፡"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ማያ ገጹን ሲከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ የእርስዎን Android TV ን ቆልፍ ወይም ሁሉንም የእርስዎን Android TV ደምስስ።"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ የኢንፎቴይንመንት ስርዓቱን ቆልፍ ወይም ሁሉንም የኢንፎቴይንመንት ስርዓት ውሂብ ደምስስ።"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"የተተየቡ ልክ ያልሆኑ የይለፍ ቃሎችን ቁጥር ተቆጣጠር፡፡ማሳያውን በምትከፍትበት ጊዜ፤ እና በጣም ብዙ ልክ ያልሆኑ የይለፍ ቃሎች ከተተየቡ ስልኩን ቆልፈው ወይም ሁሉንም የስልኩን ውሂብ ደምስሰው፡፡"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ ጡባዊውን ቆልፍ ወይም ሁሉንም የዚህን ተጠቃሚ ውሂብ ደምስስ።."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ የእርስዎ Android TV መሣሪያን ቆልፍ ወይም ሁሉንም የዚህን ተጠቃሚ ውሂብ ደምስስ።"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ የኢንፎቴይንመንት ስርዓቱን ቆልፍ ወይም ሁሉንም የዚህን ተጠቃሚ ውሂብ ደምስስ።"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ማያ ገጹን ሲያስከፍቱ በትክክል ያልተተየቡ የይለፍ ቃላት ብዛት ተከታተል፣ እና በጣም ብዙ ትክክል ያልሆኑ የይለፍ ቃላት ከተተየቡ ስልኩን ቆልፍ ወይም ሁሉንም የዚህን ተጠቃሚ ውሂብ ደምስስ።"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"የማያ ገጹን መቆለፊያ መለወጥ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"የማያ ገጽ መቆለፊያውን ለውጥ።"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ሁሉንም ውሂብ መሰረዝ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"የፋብሪካው ውሂብ ዳግም አስጀምርን በማከናወን፣ያለ ማስጠንቀቂያ የጡባዊውን ውሂብ አጥፋ።"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"የፋብሪካ ውሂብ ዳግም ቅንብርን ያለ ማስጠንቀቂያ በማከናወን የእርስዎን Android TV መሣሪያ ውሂብን ደምስስ።"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"የፋብሪካ ውሂብ ዳግም ማስጀመር በማከናወን ያለማስጠንቀቂያ የኢንፎቴይንመንት ስርዓትን ውሂብ ደምስስ።"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"የፋብሪካ ውሂብ ድጋሚ አስጀምር በማከናወን ያለ ማሰጠንቀቂያ የስልኩን ውሂብ ደምስስ።"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"የተጠቃሚ ውሂብ ደምስስ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"የመገለጫ ውሂብ ደምስስ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"የተጠቃሚ ውሂብ ደምስስ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ያለምንም ማስጠንቀቂያ የዚህን ጡባዊ የተጠቃሚ ውሂብ ደምስስ።"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"በዚህ Android TV መሣሪያ ላይ ያለ ማስጠንቀቂያ የዚህን ተጠቃሚ ውሂብ ደምስስ።"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"በዚህ የኢንፎቴይንመንት ሥርዓት ላይ ያለ ማስጠንቀቂያ የዚህን መገለጫ ውሂብ ደምስስ።"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ያለምንም ማስጠንቀቂያ የዚህን ስልክ የተጠቃሚ ውሂብ ደምስስ።"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"የመሣሪያውን ሁሉንም ፕሮክሲ አዘጋጅ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"መመሪያ ነቅቶ እያለ ጥቅም ላይ ሊውል የሚችለውን የመሣሪያውን ሁሉንተናዊ ተኪ አዘጋጅ። የመሣሪያ ባለቤት ብቻ የሁሉንተናዊ ተኪውን ማዘጋጀት ይችላል።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 863912d..0748af2 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -749,9 +749,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"مراقبة محاولات فتح قفل الشاشة"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"لمراقبة عدد مرات كتابة كلمات المرور غير الصحيحة عند فتح قفل الشاشة وتأمين الجهاز اللوحي أو مسح جميع بياناته في حالة كتابة الكثير من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وقفل جهاز Android TV أو محو جميع بيانات جهاز Android TV إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وقفل \"نظام الترفيه والمعلومات\" أو محو جميع بياناته إذا تمت كتابة عدد كبير جدًا من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وتأمين الهاتف ومحو جميع بياناته إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وتأمين الجهاز اللوحي ومحو جميع بيانات هذا المستخدم إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وقفل جهاز Android TV أو محو جميع بيانات هذا المستخدم إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وتأمين \"نظام الترفيه والمعلومات\" ومحو جميع بيانات هذا الملف الشخصي إذا تمت كتابة عدد كبير جدًا من كلمات المرور غير الصحيحة."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"يمكنك مراقبة عدد كلمات المرور غير الصحيحة التي تمت كتابتها عند فتح قفل الشاشة، وتأمين الهاتف ومحو جميع بيانات هذا المستخدم إذا تمت كتابة عدد أكبر من اللازم من كلمات المرور غير الصحيحة."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"تغيير قفل الشاشة"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"إمكانية تغيير قفل الشاشة"</string>
@@ -760,10 +762,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"محو جميع البيانات"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"يمكنك محو بيانات الجهاز اللوحي بدون تحذير، وذلك عبر إجراء إعادة الضبط على الإعدادات الأصلية."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"يمكنك محو بيانات جهاز Android TV بدون تحذير عن طريق تنفيذ إعادة الضبط على الإعدادات الأصلية."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"يمكنك محو بيانات \"نظام الترفيه والمعلومات\" بدون تحذير، وذلك من خلال إعادة الضبط على الإعدادات الأصلية."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"محو بيانات الهاتف بدون تحذير، وذلك من خلال إعادة ضبط البيانات على الإعدادات الأصلية"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"محو بيانات المستخدم"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"محو بيانات الملف الشخصي"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"محو بيانات المستخدم"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"لمحو بيانات هذا المستخدم على هذا الجهاز اللوحي بدون تحذير."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"لمحو بيانات هذا المستخدم على جهاز Android TV هذا بدون تحذير."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"يمكنك محو بيانات هذا الملف الشخصي على \"نظام الترفيه والمعلومات\" هذا بدون تحذير."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"لمحو بيانات هذا المستخدم على هذا الهاتف بدون تحذير."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"تعيين الخادم الوكيل العمومي للجهاز"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"لضبط الخادم الوكيل العام في الجهاز على الاستخدام أثناء تفعيل السياسة. ولن يمكن لأحد سوى مالك الجهاز ضبط الخادم الوكيل العام."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 818166b..4cfb88c 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্ৰীন আনলক কৰা প্ৰয়াসবোৰ নিৰীক্ষণ কৰক"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে টেবলেটটো লক কৰক বা টেবলেটটোৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে Android TV ডিভাইচটো লক কৰক অথবা আপোনাৰ Android TV ডিভাইচৰ আটাইখিনি ডেটা মচক।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"স্ক্ৰীন আনলক কৰোঁতে দিয়া অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড দিয়া হয় তেন্তে ইনফ’টেইনমেণ্ট ছিষ্টেমটো লক কৰক অথবা এই ইনফ’টেইনমেণ্ট ছিষ্টেমটোৰ আটাইবোৰ ডেটা মোহাৰক।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে ফ\'নটো লক কৰক বা ফ\'নটোৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে টেবলেটটো লক কৰক বা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"স্ক্ৰীনখন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে Android TV ডিভাইচটো লক কৰক অথবা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"স্ক্ৰীন আনলক কৰোঁতে দিয়া অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড দিয়া হয় তেন্তে ইনফ’টেইনমেণ্ট ছিষ্টেমটো লক কৰক অথবা এই প্ৰ’ফাইলটোৰ আটাইবোৰ ডেটা মোহাৰক।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"স্ক্ৰীনখন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে ফ\'নটো লক কৰক অথবা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"স্ক্ৰীন লক সলনি কৰক"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"স্ক্ৰীন লক সলনি কৰক।"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"আটাইবোৰ ডেটা মচক"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"সতৰ্কবাণী প্ৰেৰণ নকৰাকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি টেবলেটৰ ডেটা মচক।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"কোনো সতর্কবার্তা নপঠিওৱাকৈ ফেক্টৰী ডেটা ৰিছেট কৰি আপোনাৰ Android TV ডিভাইচৰ ডেটা মচক।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"সর্তকবাণী নিদিয়াকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি ইনফ’টেইনমেণ্ট ছিষ্টেমৰ ডেটা মোহাৰক।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"সতৰ্কবাণী প্ৰেৰণ নকৰাকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি ফ\'নৰ ডেটা মচক।"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ব্য়ৱহাৰকাৰীৰ তথ্য় মচক"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"প্ৰ’ফাইলৰ ডেটা মোহাৰক"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ব্য়ৱহাৰকাৰীৰ তথ্য় মচক"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"এই টেবলেটটোত থকা এই ব্যৱহাৰকাৰীৰ তথ্য কোনো সর্তকবাণী নিদিয়াকৈ মচি পেলাওক।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"কোনো সতর্কবার্তা নপঠিওৱাকৈ এই Android TV ডিভাইচটোত এই ব্যৱহাৰকাৰীৰ ডেটা মচক।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"সর্তকবাণী নিদিয়াকৈয়ে এই ইনফ’টেইনমেণ্ট ছিষ্টেমত এই প্ৰ’ফাইলটোৰ ডেটা মোহাৰক।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"এই ফ\'নটোত থকা এই ব্যৱহাৰকাৰীৰ তথ্য কোনো সর্তকবাণী নিদিয়াকৈ মচি পেলাওক।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ডিভাইচৰ বাবে গ্ল\'বেল প্ৰক্সী ছেট কৰক"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"নীতি সক্ষম কৰি থোৱা অৱস্থাত ব্য়ৱহাৰ কৰিবৰ বাবে ডিভাইচৰ বাবে গ্ল\'বেল প্ৰক্সী ছেট কৰক। কেৱল ডিভাইচৰ গৰাকীয়েহে গ্ল\'বেল প্ৰক্সী ছেট কৰিব পাৰে।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index a6098b8..136563a 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranı kiliddən çıxarmaq üçün edilən cəhdlərə nəzarət edin"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Ekan kilidini açarkən daxil edilmiş yanlış parollara baxın və əgər həddindən çox yanlış parollar daxil edilibsə, planşeti kilidləyin və ya bütün planşet datasını silin."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin, Android TV cihazını kilidləyin və ya həddən çox yanlış parol yazılıbsa, Android TV cihazının bütün datasını silin."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin və həddindən çox yanlış parol daxil edilibsə, əyləncə-məlumat sistemini kilidləyin və ya bütün əyləncə-məlumat sistemi datasını silin."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Ekan kilidini açarkən daxil edilmiş yanlış parollara baxın və əgər həddindən çox yanlış parollar daxil edilibsə, telefonu kilidləyin və ya bütün telefon datasını silin."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin və planşeti kilidləyin və ya əgər həddən çox yanlış parol yazılıbsa, həmin istifadəçinin bütün verilənlərini silin."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin, Android TV cihazını kilidləyin və ya həddən çox yanlış parol yazılıbsa, həmin istifadəçinin bütün datasını silin."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin və həddindən çox yanlış parol daxil edilibsə, əyləncə-məlumat sistemini kilidləyin və ya bu profilin bütün datasını silin."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Ekranı kiliddən çıxararkən yazılan yanlış parolların sayına nəzarət edin və telefonu kilidləyin və ya əgər həddən çox yanlış parol yazılıbsa, həmin istifadəçinin bütün verilənlərini silin."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ekran kilidini dəyişmək"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ekran kilidini dəyişmək"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Bütün məlumatları silmək"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Planşetin datasını xəbərdarlıq olmadan, zavod data sıfırlaması ilə silin."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Android TV cihazının datasını fabrik sıfırlaması haqqında xəbərdarlıq olmadan silin."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Xəbərdarlıq etmədən istehsalçı nizamlarına qaytarmaqla əyləncə-məlumat sistemi datasını silin."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Telefondakı bütün məlumatları xəbərdarlıqsız sıfırlayaraq məhv etmək"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"İstifadəçi verilənlərini sil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profil datasını silin"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"İstifadəçi verilənlərini sil"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Xəbərdarlıq etmədən bu istifadəçinin verilənlərini bu planşetdə silin."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Bu istifadəçinin datasını xəbərdarlıq olmadan Android TV cihazında silin."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Xəbərdarlıq etmədən bu əyləncə-məlumat sistemində bu profilin datasını silin."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Xəbərdarlıq etmədən bu istifadəçinin bu telefondakı verilənlərini silin."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Cihazın qlobal proksisini ayarlayın"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Siyasət aktivləşdirilən zaman istifadə edilmək üçün cihazın qlobal proksisini təyin edin. Yalnız cihazın sahibi qlobal proksini təyin edə bilər."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0219377..00fc2b5 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -740,9 +740,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadgledajte pokušaje otključavanja ekrana"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Prati broj netačno unetih lozinki prilikom otključavanja ekrana i zaključava tablet ili briše podatke sa tableta ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava Android TV uređaj ili briše sve podatke sa Android TV uređaja ako se unese previše netačnih lozinki."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava sistem za info-zabavu ili briše sve podatke sa sistema za info-zabavu ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava telefon ili briše sve podatke sa telefona ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava tablet ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava Android TV uređaj ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava sistem za info-zabavu ili briše sve podatke ovog profila ako se unese previše netačnih lozinki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava telefon ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Promena zaključavanja ekrana"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Menja zaključavanje ekrana."</string>
@@ -751,10 +753,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje svih podataka"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Brisanje podataka na tabletu bez upozorenja resetovanjem na fabrička podešavanja."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Briše podatke Android TV uređaja bez upozorenja pomoću resetovanja na fabrička podešavanja."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Briše podatke na sistemu za info-zabavu bez upozorenja resetovanjem na fabrička podešavanja."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Brisanje podataka na telefonu bez upozorenja resetovanjem na fabrička podešavanja."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Obriši podatke korisnika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Brisanje podataka profila"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Obriši podatke korisnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Briše podatke ovog korisnika na ovom tabletu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Briše podatke ovog korisnika na ovom Android TV uređaju bez upozorenja."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Briše podatke ovog profila na ovom sistemu za info-zabavu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Briše podatke ovog korisnika na ovom telefonu bez upozorenja."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Podesite globalni proksi server uređaja"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Podešava globalni proksi uređaja koji će se koristiti dok su smernice omogućene. Samo vlasnik uređaja može da podesi globalni proksi."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 0f47465..0157e66 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Сачыць за спробамі разблакіроўкі экрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Сачыць за колькасцю няправільных набраных пароляў падчас разблакоўкі экрана і блакаваць планшэт або сціраць усе дадзеныя на ім, калі няправільны пароль набраны занадта шмат разоў."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Сачыць за колькасцю няправільна набраных пароляў падчас разблакіроўкі экрана і заблакіраваць прыладу Android TV або сцерці ўсе даныя на прыладзе, калі няправільны пароль набраны занадта шмат разоў."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Падчас разблакіроўкі экрана сачыць за колькасцю няправільна набраных пароляў і, калі няправільны пароль набраны занадта шмат разоў, заблакіраваць інфармацыйна-забаўляльную сістэму ці сцерці ў ёй усе даныя."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Сачыць за колькасцю няправільных набраных пароляў падчас разблакоўкі экрана і блакаваць тяэлефон або сціраць усе дадзеныя на ім, калі набрана занадта шмат няправільных пароляў."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Сачыць за колькасцю няправільна набраных пароляў падчас разблакіроўкі экрана і блакіраваць планшэт або сцерці ўсе даныя гэтага карыстальніка, калі няправільны пароль набраны занадта шмат разоў."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Сачыць за колькасцю няправільна набраных пароляў падчас разблакіроўкі экрана і заблакіраваць прыладу Android TV або сцерці ўсе даныя карыстальніка, калі няправільны пароль набраны занадта шмат разоў."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Падчас разблакіроўкі экрана сачыць за колькасцю няправільна набраных пароляў і, калі няправільны пароль набраны занадта шмат разоў, заблакіраваць інфармацыйна-забаўляльную сістэму ці сцерці ўсе даныя гэтага профілю."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Сачыць за колькасцю няправільна набраных пароляў падчас разблакіроўкі экрана і блакіраваць тэлефон або сцерці ўсе даныя гэтага карыстальніка, калі няправільны пароль набраны занадта шмат разоў."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Змяніць блакіроўку экрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Змяніць блакіроўку экрана."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Сцерці ўсе даныя"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Cцерці даныя з планшэта без папярэджання, выканаўшы скід да заводскіх даных."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Сцерці даныя з прылады Android TV без папярэджання, выканаўшы скід да заводскіх налад."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Падчас скіду да заводскіх налад сцерці даныя ў інфармацыйна-забаўляльнай сістэме без папярэджання."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Сцерці даныя з тэлефона без папярэджання, выканаўшы скід да заводскіх налад."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Сцерці карыстальніцкія даныя"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Сцерці даныя профілю"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Сцерці карыстальніцкія даныя"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Сцерці даныя гэтага карыстальніка на дадзеным планшэце без папярэджання."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Сцерці даныя карыстальніка з гэтай прылады Android TV без папярэджання."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Сцерці даныя гэтага профілю ў гэтай інфармацыйна-забаўляльнай сістэме без папярэджання."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Сцерці даныя гэтага карыстальніка на дадзеным тэлефоне без папярэджання."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Усталяваць глабальны проксі прылады"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Задаць агульны проксі-cервер прылады, які будзе выкарыстоўвацца, калі прылада актыўная. Толькі ўладальнік прылады можа задаць агульны проксі-сервер."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 256ab09..d75d2e7 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Наблюдаване на опитите за отключване на екрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Наблюдава броя въведени неправилни пароли при отключването на екрана и заключва таблета или изтрива всички данни от него, ако неправилните пароли са твърде много."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Следене на броя на неправилно въведените пароли при отключване на екрана и заключване на устройството ви с Android TV или изтриване на всички данни от него, ако неуспешните опити са твърде много."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Наблюдава броя на неправилно въведените пароли при отключването на екрана и заключва основното устройство или изтрива всички данни от него, ако неуспешните опити са твърде много."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Наблюдава броя въведени неправилни пароли при отключването на екрана и заключва телефона или изтрива всички данни от него, ако неправилните пароли са твърде много."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Наблюдава броя на неправилно въведените пароли при отключване на екрана и заключва таблета или изтрива всички данни на този потребител, ако неуспешните опити са твърде много."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Следене на броя на неправилно въведените пароли при отключване на екрана и заключване на устройството ви с Android TV или изтриване на всички данни на този потребител, ако неуспешните опити са твърде много."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Наблюдава броя на неправилно въведените пароли при отключване на екрана и заключва основното устройство или изтрива всички данни в този потребителски профил, ако неуспешните опити са твърде много."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Наблюдава броя на неправилно въведените пароли при отключване на екрана и заключва телефона или изтрива всички данни на този потребител, ако неуспешните опити са твърде много."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Промяна на заключването на екрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Променя заключването на екрана."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Изтриване на всички данни"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Изтриване на данните в таблета без предупреждение чрез възстановяване на фабричните настройки."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Изтриване на данните от устройството ви с Android TV без предупреждение чрез възстановяване на фабричните настройки."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Изтрива данните на основното устройство без предупреждение чрез възстановяване на фабричните настройки."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Изтрива данните в телефона без предупреждение чрез възстановяване на фабричните настройки."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Изтриване на потребителските данни"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Изтриване на данните в потребителския профил"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Изтриване на потребителските данни"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Изтрива данните на този потребител от таблета без предупреждение."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Изтриване на данните на този потребител от устройството с Android TV без предупреждение."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Изтрива данните в този потребителски профил на основното устройство без предупреждение."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Изтрива данните на този потребител от телефона без предупреждение."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Задаване на глобален прокси сървър за устройството"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Задава глобалния прокси сървър за устройството, който да се използва, когато правилото е активирано. Само собственикът на устройството може да задава такъв сървър."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 5e54b61..3080948 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্রিন আনলক করার প্রচেষ্টাগুলির উপরে নজর রাখুন"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"স্ক্রীণ আনলক করার সময় ভুলভাবে লেখা পাসওয়ার্ড প্রবেশের সংখ্যা মনিটার করে, এবং ট্যাবলেট লক করে এবং অনেক বার পাসওয়ার্ড ভুল ভাবে লেখা হলে ট্যাবলেটের ডেটা মুছে ফেলে৷"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"স্ক্রিন আনলক করার সময় কতবার ভুল পাসওয়ার্ড লেখা হচ্ছে তা মনিটর করুন এবং Android TV ডিভাইস লক করুন অথবা অনেকবার ভুল পাসওয়ার্ড লেখা হলে ডিভাইসের সব ডেটা মুছে ফেলুন।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ফোনের স্ক্রিন আনলক করার সময় কতবার ভুল পাসওয়ার্ড টাইপ করা হয়েছে তা মনিটর করুন। একাধিকবার ভুল পাসওয়ার্ড টাইপ করা হলে ইনফোটেইনমেন্ট সিস্টেম লক করুন অথবা এর সব ডেটা মুছে ফেলুন।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"স্ক্রীণ আনলক করার সময় ভুলভাবে লেখা পাসওয়ার্ড প্রবেশের সংখ্যা মনিটার করে, এবং ফোন লক করে এবং অনেক বার পাসওয়ার্ড ভুল ভাবে লেখা হলে ফোনের ডেটা মুছে ফেলে৷"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"স্ক্রিন আনলক করার সময় ভুলভাবে লেখা পাসওয়ার্ড প্রবেশের সংখ্যা মনিটার করে, এবং ট্যাবলেট লক করে এবং অনেক বার পাসওয়ার্ড ভুল ভাবে লেখা হলে ব্যবহারকারীর ডেটা মুছে ফেলে৷"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"স্ক্রিন আনলক করার সময় কতবার ভুল পাসওয়ার্ড লেখা হচ্ছে তা মনিটর করুন এবং Android TV ডিভাইস লক করুন অথবা অনেকবার ভুল পাসওয়ার্ড লেখা হলে সব ব্যবহারকারীর ডেটা মুছে ফেলুন।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ফোনের স্ক্রিন আনলক করার সময় কতবার ভুল পাসওয়ার্ড টাইপ করা হয়েছে তা মনিটর করুন। একাধিকবার ভুল পাসওয়ার্ড টাইপ করা হলে ইনফোটেইনমেন্ট সিস্টেম লক করুন অথবা এই প্রোফাইলের সব ডেটা মুছে ফেলুন।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"স্ক্রিন আনলক করার সময় ভুলভাবে লেখা পাসওয়ার্ড প্রবেশের সংখ্যা মনিটার করে, এবং ফোন লক করে এবং অনেক বার পাসওয়ার্ড ভুল ভাবে লেখা হলে ব্যবহারকারীর ডেটা মুছে ফেলে৷"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"স্ক্রিন লক পরিবর্তন করে"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"স্ক্রিন লক পরিবর্তন করুন৷"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"সমস্ত ডেটা মুছে দেয়"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ফ্যাক্টরি ডেটা আবার সেট কার্য সম্পাদনার দ্বারা কোনো রকম সতর্কতা ছাড়াই ট্যাবলেটের ডেটা মোছে৷"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ফ্যাক্টরি ডেটা রিসেট করে কোনও সতর্কতা ছাড়াই আপনার Android TV ডিভাইসের ডেটা মুছে ফেলুন।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ফ্যাক্টরি ডেটা রিসেট করে কোনও সতর্কতা মূলক বিজ্ঞপ্তি ছাড়াই আপনার ইনফোটেইনমেন্ট সিস্টেমের ডেটা মুছে ফেলুন।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ফ্যাক্টরি ডেটা রিসেট করে কোনও সতর্কতা ছাড়াই ফোনের ডেটা মোছে৷"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ব্যবহারকারীর ডেটা মুছুন"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"প্রোফাইল ডেটা মুছে ফেলুন"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ব্যবহারকারীর ডেটা মুছুন"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"সতর্কীকরণ ছাড়াই এই ট্যাবলেটে থাকা ব্যাবহারকার্রী ডেটা মুছে ফেলে৷"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"কোনও সতর্কতা ছাড়াই এই ব্যবহারকারীর ডেটা Android TV ডিভাইসটি থেকে মুছুন।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"কোনও সতর্কতা মূলক বিজ্ঞপ্তি ছাড়াই ইনফোটেইনমেন্ট সিস্টেমে থাকা এই প্রোফাইলের ডেটা মুছে ফেলুন।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"সতর্কীকরণ ছাড়াই এই ফোনে থাকা ব্যাবহারকার্রী ডেটা মুছে ফেলে৷"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ডিভাইসের বৈশ্বিক প্রক্সী সেট করে"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"নীতিযখন নীতি সক্ষম করা হয় তখন ডিভাইসের বৈশ্বিক প্রক্সী ব্যবহার করা হবে সেই হিসেবে সেট করে৷ শুধুমাত্র ডিভাইসের মালিক বৈশ্বিক প্রক্সী সেট করতে পারেন৷"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index e6bfadcb..ea37490 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -740,9 +740,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Prati pokušaje otključavanja ekrana"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Prati broj pogrešno unijetih lozinki prilikom otključavanja ekrana i zaključava tablet ili briše sve podatke s njega ukoliko se previše puta unese pogrešna lozinka."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Praćenje broja unosa netačnih lozinki pri otključavanju ekrana i zaključavanje Android TV uređaja ili brisanje svih podataka Android TV uređaja u slučaju prevelikog broja unosa netačnih lozinki."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati koliko puta je lozinka neispravno unijeta prilikom otključavanja ekrana i zaključava informativno-zabavni sistem ili briše sve podatke informativno-zabavnog sistema ako se lozinka neispravno unese previše puta."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Prati broj pogrešno unesenih lozinki prilikom otključavanja ekrana i zaključava telefon ili briše sve podatke s telefona ukoliko se previše puta unese pogrešna lozinka."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Prati broj neispravnih lozinki koje su unijete za otključavanje ekrana te zaključava tablet ili briše sve podatke ovog korisnika ukoliko je unijeto previše neispravnih lozinki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Praćenje broja unosa netačnih lozinki za otključavanje ekrana te zaključavanje Android TV uređaja ili brisanje svih podataka ovog korisnika u slučaju prekomjernog unosa netačnih lozinki."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Prati koliko puta je lozinka neispravno unijeta prilikom otključavanja ekrana i zaključava informativno-zabavni sistem ili briše sve podatke ovog profila ako se lozinka neispravno unese previše puta."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Prati broj neispravnih lozinki koje su unijete za otključavanje ekrana te zaključava telefon ili briše sve podatke ovog korisnika ukoliko je unijeto previše neispravnih lozinki."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Promjena zaključavanja ekrana"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Mijenja zaključavanje ekrana."</string>
@@ -751,10 +753,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje svih podataka"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Briše podatke s tableta bez upozorenja tako što ga vraća na fabričke postavke."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Brisanje podataka Android TV uređaja bez upozorenja vraćanjem uređaja na fabričke postavke."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Bez upozorenja briše podatke informativno-zabavnog sistema vraćanjem na fabričke postavke."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Briše podatke s telefona bez upozorenja vraćanjem telefona na fabričke postavke."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Izbriši podatke korisnika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Briše podatke profila"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Izbriši podatke korisnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Bez upozorenja briše podatke ovog korisnika sa ovog tableta."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Brisanje podataka ovog korisnika na Android TV uređaju bez upozorenja."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bez upozorenja briše podatke ovog profila na ovom informativno-zabavnom sistemu."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Bez upozorenja briše podatke ovog korisnika sa ovog telefona."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Postavlja globalni proksi uređaja"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Postavlja globalni proksi uređaja koji će se koristiti dok su smjernice omogućene. Samo vlasnik uređaja može postaviti globalni proksi."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 89e466a..91ba2f0 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar els intents de desbloqueig de la pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Supervisa el nombre de contrasenyes incorrectes introduïdes per desbloquejar la pantalla i bloqueja la tauleta o n\'esborra totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han introduït en intentar desbloquejar la pantalla i bloqueja el dispositiu Android TV o esborra totes les dades del dispositiu si s\'introdueixen massa contrasenyes incorrectes."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Supervisa el nombre de contrasenyes incorrectes introduïdes en desbloquejar la pantalla, i bloqueja el sistema d\'informació i entreteniment o n\'esborra totes les dades si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisa el nombre de contrasenyes incorrectes introduïdes en desbloquejar la pantalla, i bloqueja el telèfon o esborra totes les dades del telèfon si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han escrit en intentar desbloquejar la pantalla i bloqueja la tauleta o n\'esborra totes les dades de l\'usuari si s\'escriuen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han introduït en intentar desbloquejar la pantalla i bloqueja el dispositiu Android TV o n\'esborra totes les dades de l\'usuari si s\'introdueixen massa contrasenyes incorrectes."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Supervisa el nombre de contrasenyes incorrectes introduïdes en desbloquejar la pantalla, i bloqueja el sistema d\'informació i entreteniment o esborra totes les dades d\'aquest perfil si s\'introdueixen massa contrasenyes incorrectes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han escrit en intentar desbloquejar la pantalla i bloqueja el telèfon o n\'esborra totes les dades de l\'usuari si s\'escriuen massa contrasenyes incorrectes."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Canviar el bloqueig de pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Canvia el bloqueig de pantalla."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Esborrar totes les dades"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Esborra les dades de la tauleta sense avisar, i restableix les dades de fàbrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Suprimeix les dades del dispositiu Android TV sense previ avís mitjançant el restabliment de les dades de fàbrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Esborra les dades del sistema d\'informació i entreteniment sense avisar mitjançant el restabliment de les dades de fàbrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Esborra les dades del telèfon sense avisar, i restableix les dades de fàbrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Esborrar les dades de l\'usuari"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Esborra les dades del perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Esborrar les dades de l\'usuari"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Esborra les dades de l\'usuari desades a la tauleta sense avisar-ne."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Esborra les dades de l\'usuari desades al dispositiu Android TV sense previ avís."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Esborra les dades del perfil d\'aquest sistema d\'informació i entreteniment sense avisar."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Esborra les dades de l\'usuari desades al telèfon sense avisar-ne."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir el servidor intermediari global del dispositiu"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Si la política s\'activa, s\'utilitza el servidor intermediari global del dispositiu. Només el propietari del dispositiu el pot establir."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4f9e7dc..c928655 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovat pokusy o odemknutí obrazovky"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout tablet nebo vymazat z tabletu všechna data, pokud bylo zadáno příliš mnoho nesprávných hesel."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud jich bude zadáno příliš mnoho, uzamknout zařízení Android TV nebo z něj vymazat všechna data."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout informační a zábavní systém nebo vymazat veškerá data v informačním a zábavním systému, pokud je zadáno příliš mnoho nesprávných hesel."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky a uzamknout telefon nebo vymazat z telefonu všechna data, pokud bylo zadáno příliš mnoho nesprávných hesel."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout tablet nebo vymazat veškerá data uživatele."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Sledovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud jich bude zadáno příliš mnoho, uzamknout zařízení Android TV nebo z něj vymazat všechna data tohoto uživatele."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout informační a zábavní systém nebo vymazat veškerá data profilu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorovat počet nesprávných hesel zadaných při odemykání obrazovky, a pokud je zadáno příliš mnoho nesprávných hesel, uzamknout telefon nebo vymazat veškerá data uživatele."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Změnit zámek obrazovky"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Změní se zámek obrazovky."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Vymazat všechna data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Bez upozornění smazat všechna data tabletu obnovením továrních dat."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Provést obnovení továrních dat a bez upozornění tím vymazat data v zařízení Android TV."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Bez upozornění se smažou všechna data informačního a zábavního systému obnovením továrních dat."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Bez upozornění se smažou všechna data telefonu obnovením továrních dat."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Vymazat data uživatele"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Vymazání profilových dat"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Vymazat data uživatele"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Vymazat data tohoto uživatele v tomto tabletu bez upozornění."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Bez upozornění vymazat data tohoto uživatele v tomto zařízení Android TV."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bez upozornění se smažou data tohoto profilu v informačním a zábavním systému."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Vymazat data tohoto uživatele v tomto telefonu bez upozornění."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Nastavit globální proxy server zařízení"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Nastaví globální proxy server, který bude používán, když je zásada zapnuta. Globální proxy server může nastavit pouze vlastník zařízení."</string>
@@ -1000,7 +1005,7 @@
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nOpravdu tuto stránku chcete opustit?"</string>
<string name="save_password_label" msgid="9161712335355510035">"Potvrdit"</string>
<string name="double_tap_toast" msgid="7065519579174882778">"Tip: Dvojitým klepnutím můžete zobrazení přiblížit nebo oddálit."</string>
- <string name="autofill_this_form" msgid="3187132440451621492">"Aut.vyp."</string>
+ <string name="autofill_this_form" msgid="3187132440451621492">"Autofill"</string>
<string name="setup_autofill" msgid="5431369130866618567">"Nastav aut. vyp."</string>
<string name="autofill_window_title" msgid="4379134104008111961">"Automatické vyplňování pomocí služby <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
<string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index b868ff8..4c60718 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåg forsøg på oplåsning af skærm"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Overvåg antallet af forkert indtastede adgangskoder, når du låser skærmen op, og lås din tablet, eller slet alle data i den, hvis der er indtastet for mange forkerte adgangskoder."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Registrer antallet af forkerte adgangskoder, der angives ved oplåsning af skærmen, og lås din Android TV-enhed, eller ryd alle dataene på din Android TV-enhed, hvis adgangskoden angives forkert for mange gange."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Registrer antallet af forkert indtastede adgangskoder, når du låser skærmen op, og lås infotainmentsystemet, eller slet alle data i infotainmentsystemet, hvis der er indtastet for mange forkerte adgangskoder."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Overvåg antallet af forkerte adgangskoder ved oplåsning af skærmen, og lås telefonen eller slet alle data på telefonen, hvis der er indtastet for mange forkerte adgangskoder."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Registrer antallet af forkerte adgangskoder, der angives ved oplåsning af skærmen, og lås din tablet, eller slet alle brugerens data, hvis adgangskoden tastes forkert for mange gange."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Registrer antallet af forkerte adgangskoder, der angives ved oplåsning af skærmen, og lås din Android TV-enhed, eller ryd alle brugerens data, hvis adgangskoden angives forkert for mange gange."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Registrer antallet af forkert indtastede adgangskoder, når du låser skærmen op, og lås infotainmentsystemet, eller slet alle data på denne profil, hvis der er indtastet for mange forkerte adgangskoder."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Registrer antallet af forkerte adgangskoder, der angives ved oplåsning af skærmen, og lås telefonen, eller slet alle brugerens data, hvis adgangskoden tastes forkert for mange gange."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Skifte skærmlås"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Skifter skærmlåsen."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Slette alle data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Slet din tablets data uden varsel ved at gendanne fabriksindstillingerne."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ryd dataene på din Android TV-enhed uden at gendanne fabriksdataene."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ryd infotainmentsystemets data uden varsel ved at gendanne fabriksindstillingerne."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Sletter telefonens data uden varsel ved at gendanne fabriksindstillingerne."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Slet brugerdata"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Ryd profildata"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Slet brugerdata"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Slet denne brugers data på denne tablet uden varsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ryd denne brugers data på denne Android TV-enhed uden varsel."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ryd denne profils data i dette infotainmentsystem uden varsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Slet denne brugers data på denne telefon uden varsel."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Angiv enhedens globale proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Indstil den globale proxy for enheden, der skal bruges, mens politikken er aktiveret. Det er kun enhedens ejer, der kan indstille den globale proxy."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index d325ae3..25ba98b 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Versuche zum Entsperren des Displays überwachen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays überwachen und Tablet sperren oder alle Daten auf dem Tablet löschen, wenn zu häufig ein falsches Passwort eingegeben wird."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Es wird überwacht, wie oft beim Versuch, den Bildschirm zu entsperren, ein falsches Passwort eingegeben wird. Wenn es zu viele Fehlversuche gibt, wird das Android TV-Gerät gesperrt oder alle Daten darauf werden gelöscht."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays erfassen und Infotainmentsystem sperren oder alle Daten des Infotainmentsystems löschen, wenn zu häufig ein falsches Passwort eingegeben wird."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Bildschirms überwachen und Telefon sperren oder alle Daten auf dem Telefon löschen, wenn zu häufig ein falsches Passwort eingegeben wird."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays überwachen und Tablet sperren oder alle Daten dieses Nutzers löschen, wenn zu häufig ein falsches Passwort eingegeben wird"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Es wird überwacht, wie oft beim Versuch, den Bildschirm zu entsperren, ein falsches Passwort eingegeben wird. Wenn es zu viele Fehlversuche gibt, wird das Android TV-Gerät gesperrt oder alle Daten dieses Nutzers werden gelöscht."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays erfassen und Infotainmentsystem sperren oder alle Daten dieses Profils löschen, wenn zu häufig ein falsches Passwort eingegeben wird."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Anzahl der falsch eingegebenen Passwörter beim Entsperren des Displays überwachen und Smartphone sperren oder alle Daten dieses Nutzers löschen, wenn zu häufig ein falsches Passwort eingegeben wird"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Displaysperre ändern"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ändern der Displaysperre"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Alle Daten löschen"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Auf Werkseinstellungen zurücksetzen und Daten auf dem Tablet ohne Warnung löschen"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Du kannst die Daten auf deinem Android TV-Gerät ohne vorherige Warnung löschen, indem du es auf die Werkseinstellungen zurücksetzt."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Gerät auf Werkseinstellungen zurücksetzen und damit Daten des Infotainmentsystems ohne Warnung löschen."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Gerät auf Werkseinstellungen zurücksetzen und damit Daten auf dem Telefon ohne Warnung löschen"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Nutzerdaten löschen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profildaten löschen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Nutzerdaten löschen"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Daten dieses Nutzers auf diesem Tablet ohne vorherige Warnung löschen"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Daten dieses Nutzers auf diesem Android TV-Gerät werden ohne vorherige Warnung gelöscht."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Daten dieses Profils in diesem Infotainmentsystem ohne Warnung löschen."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Daten dieses Nutzers auf diesem Smartphone ohne vorherige Warnung löschen"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Den globalen Proxy des Geräts festlegen"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Bei aktivierter Richtlinie zu verwendenden globalen Geräteproxy festlegen. Nur der Eigentümer des Geräts kann den globalen Proxy festlegen."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index b3a556d..4764e6a 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Παρακολούθηση του αριθμού λανθασμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλείδωμα του tablet ή διαγραφή όλων των δεδομένων του σε περίπτωση πληκτρολόγησης πάρα πολλών εσφαλμένων κωδικών πρόσβασης."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Παρακολουθήστε τον αριθμό των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλειδώστε τη συσκευή Android TV ή διαγράψτε όλα τα δεδομένα της σε περίπτωση εισαγωγής εσφαλμένων κωδικών πρόσβασης πάρα πολλές φορές."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Παρακολούθηση του αριθμού των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλείδωμα του συστήματος ενημέρωσης και ψυχαγωγίας ή διαγραφή όλων των δεδομένων του εάν πληκτρολογηθούν πολλοί εσφαλμένοι κωδικοί πρόσβασης."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Παρακολούθηση του αριθμού λανθασμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλείδωμα του τηλεφώνου ή διαγραφή όλων των δεδομένων του σε περίπτωση πληκτρολόγησης πάρα πολλών εσφαλμένων κωδικών πρόσβασης."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Παρακολουθήστε τον αριθμό των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλειδώστε το tablet ή διαγράψτε όλα τα δεδομένα του χρήστη, σε περίπτωση εισαγωγής πάρα πολλών εσφαλμένων κωδικών πρόσβασης."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Παρακολουθήστε τον αριθμό των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλειδώστε τη συσκευή Android TV ή διαγράψτε όλα τα δεδομένα χρήστη σε περίπτωση εισαγωγής εσφαλμένων κωδικών πρόσβασης πάρα πολλές φορές."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Παρακολούθηση του αριθμού των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλείδωμα του συστήματος ενημέρωσης και ψυχαγωγίας ή διαγραφή όλων των δεδομένων αυτού του προφίλ εάν πληκτρολογηθούν πολλοί εσφαλμένοι κωδικοί πρόσβασης."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Παρακολουθήστε τον αριθμό των εσφαλμένων κωδικών πρόσβασης που πληκτρολογούνται κατά το ξεκλείδωμα της οθόνης και κλειδώστε το τηλέφωνο ή διαγράψτε όλα τα δεδομένα του χρήστη, σε περίπτωση εισαγωγής πάρα πολλών εσφαλμένων κωδικών πρόσβασης."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Αλλαγή του κλειδώματος οθόνης"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Αλλαγή του κλειδώματος οθόνης."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Διαγραφή όλων των δεδομένων"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Διαγραφή των δεδομένων του tablet χωρίς προειδοποίηση με επαναφορά των εργοστασιακών ρυθμίσεων."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Διαγράψτε τα δεδομένα της συσκευής σας Android TV χωρίς προειδοποίηση εκτελώντας επαναφορά των εργοστασιακών δεδομένων."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Διαγραφή των δεδομένων του συστήματος ενημέρωσης και ψυχαγωγίας χωρίς προειδοποίηση με εκτέλεση επαναφοράς εργοστασιακών ρυθμίσεων."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Διαγραφή των δεδομένων του τηλεφώνου χωρίς προειδοποίηση με επαναφορά των εργοστασιακών ρυθμίσεων."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Διαγραφή δεδομένων χρήστη"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Διαγραφή δεδομένων προφίλ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Διαγραφή δεδομένων χρήστη"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Διαγραφή των δεδομένων αυτού του χρήστη σε αυτό το tablet χωρίς προειδοποίηση."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Διαγράψτε τα δεδομένα χρήστη σε αυτή τη συσκευή Android TV χωρίς προειδοποίηση."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Διαγραφή των δεδομένων αυτού του προφίλ σε αυτό το σύστημα ενημέρωσης και ψυχαγωγίας χωρίς προειδοποίηση."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Διαγραφή των δεδομένων αυτού του χρήστη σε αυτό το τηλέφωνο χωρίς προειδοποίηση."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Ρύθμιση του γενικού διακομιστή μεσολάβησης της συσκευής"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Ρυθμίστε τη χρήση του γενικού διακομιστή μεσολάβησης της συσκευής, ενώ η πολιτική είναι ενεργοποιημένη. Μόνο ο κάτοχος της συσκευής μπορεί να ρυθμίσει τον γενικό διακομιστής μεσολάβησης."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 53753aa..a7647b6 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Delete all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Delete your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Delete this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index bbeb426..8c28433 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Erase all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Delete your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Delete this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 16de6b2..6e25954 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Delete all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Delete your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Delete this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 0b19c5c..53031c6 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed when unlocking the screen and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or delete all of this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Delete all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Delete your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Delete this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 7fba349..0f0e35a 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all the tablet\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or erase all your Android TV device\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitor the number of incorrect passwords typed. when unlocking the screen, and lock the infotainment system or erase all the infotainment system\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitor the number of incorrect passwords typed. when unlocking the screen, and lock the phone or erase all the phone\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the tablet or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock your Android TV device or erase all this user\'s data if too many incorrect passwords are typed."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the infotainment system or erase all this profile\'s data if too many incorrect passwords are typed."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitor the number of incorrect passwords typed when unlocking the screen, and lock the phone or erase all this user\'s data if too many incorrect passwords are typed."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Change the screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Change the screen lock."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Erase all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Erase the tablet\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Erase your Android TV device\'s data without warning by performing a factory data reset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Erase the infotainment system\'s data without warning by performing a factory data reset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Erase the phone\'s data without warning by performing a factory data reset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Erase user data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Erase profile data"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Erase user data"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Erase this user\'s data on this tablet without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Erase this user\'s data on this Android TV device without warning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Erase this profile\'s data on this infotainment system without warning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Erase this user\'s data on this phone without warning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Set the device global proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Set the device global proxy to be used while policy is enabled. Only the device owner can set the global proxy."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 38b4963..e7110c9 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisa los intentos para desbloquear la pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Controla la cantidad de contraseñas incorrectas ingresadas al desbloquear la pantalla y bloquea la tablet o borra todos los datos de la tablet si se ingresaron demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Supervisa la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquea el dispositivo Android TV o borra todos sus datos si se ingresan demasiadas contraseñas incorrectas."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear el sistema de infoentretenimiento, o borrar todos los datos del sistema, si se ingresaron demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisar la cantidad de contraseñas ingresadas incorrectamente al desbloquear la pantalla, y bloquear el dispositivo o borrar todos sus datos si se ingresan demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear la tablet, o borrar todos los datos del usuario, si se ingresan demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Supervisa la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquea el dispositivo Android TV o borra todos los datos del usuario si se ingresan demasiadas contraseñas incorrectas."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear el sistema de infoentretenimiento, o borrar todos los datos de perfil, si se ingresaron demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Permite controlar la cantidad de contraseñas incorrectas que se escriben al desbloquear la pantalla y bloquear el teléfono, o borrar todos los datos del usuario, si se ingresan demasiadas contraseñas incorrectas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Cambiar el bloqueo de pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Cambia el bloqueo de pantalla."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Borrar todos los datos"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Eliminar los datos de la tablet sin avisar y restablecer la configuración de fábrica"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Restablece la configuración de fábrica para borrar los datos del dispositivo Android TV sin previo aviso."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Permite borrar los datos del sistema de infoentretenimiento sin previo aviso mediante el restablecimiento de la configuración de fábrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Borra los datos del dispositivo sin avisar y restablece la configuración de fábrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Borrar los datos del usuario"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Borrar los datos de perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Borrar los datos del usuario"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Permite borrar los datos del usuario en esta tablet sin previo aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Borra los datos del usuario en este dispositivo Android TV sin previo aviso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Permite borrar los datos de perfil en este sistema de infoentretenimiento sin previo aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Permite borrar los datos del usuario en este teléfono sin previo aviso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Configura el proxy global de dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Configura el proxy global de dispositivo que se usará mientras se habilita la política. Solo el propietario del dispositivo puede configurar el proxy global."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 5eca42f..a582a09 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar los intentos de desbloqueo de pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Controla el número de contraseñas incorrectas introducidas al desbloquear la pantalla y bloquea el tablet o elimina todos sus datos si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Comprueba cuántas veces se han introducido contraseñas incorrectas para desbloquear la pantalla y, si te parece que han sido demasiadas, bloquea tu dispositivo Android TV o borra todos sus datos."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Controla el número de contraseñas incorrectas introducidas al desbloquear la pantalla y bloquea el sistema de infoentretenimiento o borra todos sus datos si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Controla el número de contraseñas incorrectas introducidas al desbloquear la pantalla y bloquea el teléfono o elimina todos sus datos si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Controla el número de contraseñas incorrectas introducidas para desbloquear la pantalla y bloquea el tablet o borra todos los datos del usuario si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Comprueba cuántas veces se han introducido contraseñas incorrectas para desbloquear la pantalla y, si te parece que han sido demasiadas, bloquea tu dispositivo Android TV o borra todos los datos de este usuario."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Controla el número de contraseñas incorrectas introducidas al desbloquear la pantalla y bloquea el sistema de infoentretenimiento o borra todos los datos del perfil si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Controla el número de contraseñas incorrectas introducidas para desbloquear la pantalla y bloquea el teléfono o borra todos los datos del usuario si se introducen demasiadas contraseñas incorrectas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Cambiar el bloqueo de pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Cambia el bloqueo de pantalla"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Borrar todos los datos"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Borrar los datos del tablet sin avisar restableciendo el estado de fábrica"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Restablece los datos de fábrica de tu dispositivo Android TV, eliminando sin previo aviso los datos que tuviera."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Borra los datos del sistema de infoentretenimiento sin avisar restableciendo el estado de fábrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Borra los datos del teléfono sin avisar restableciendo el estado de fábrica"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Borrar datos del usuario"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Borrar datos del perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Borrar datos del usuario"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Borra los datos del usuario en este tablet sin avisar."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Eliminar los datos de este usuario del dispositivo Android TV sin previo aviso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Borra los datos del perfil de este sistema de infoentretenimiento sin avisar."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Borra los datos del usuario en este teléfono sin avisar."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir el servidor proxy global"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Define el servidor proxy global que se debe utilizar mientras la política esté habilitada. Solo el propietario del dispositivo puede definir el proxy global."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 3320ab2..84912d8 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekraani avamiskatsete jälgimine"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Jälgib ekraani avamisel valesti sisestatud paroolide arvu ja lukustab tahvelarvuti või kustutab kõik selle andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Jälgitakse ekraanikuva avamisel sisestatud valede paroolide arvu ja lukustatakse Android TV seade või kustutatakse kõik Android TV seadme andmed, kui vale parool sisestatakse liiga palju kordi."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Jälgib ekraani avamisel valesti sisestatud paroolide arvu ning lukustab teabe ja meelelahutuse süsteemi või kustutab kõik selle andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Jälgib ekraani avamisel valesti sisestatud paroolide arvu ja lukustab telefoni või kustutab kõik selle andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Jälgitakse ekraani avamisel sisestatud valede paroolide arvu ja lukustatakse tahvelarvuti või kustutatakse kõik selle kasutaja andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Jälgitakse ekraanikuva avamisel sisestatud valede paroolide arvu ja lukustatakse Android TV seade või kustutatakse kõik selle kasutaja andmed, kui vale parool sisestatakse liiga palju kordi."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Jälgitakse ekraani avamisel sisestatud valede paroolide arvu ning lukustatakse teabe ja meelelahutuse süsteem või kustutatakse kõik selle profiili andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Jälgitakse ekraani avamisel sisestatud valede paroolide arvu ja lukustatakse telefon või kustutatakse kõik selle kasutaja andmed, kui vale parool sisestatakse liiga palju kordi."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ekraaniluku muutmine"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Muutke ekraanilukku."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Kõikide andmete kustutamine"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Kustutage tahvelarvuti andmed hoiatamata, lähtestades arvuti tehaseandmetele."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Kustutatakse teie Android TV seadme andmed ilma hoiatamata, lähtestades seadme tehase andmetele."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Teabe ja meelelahutuse süsteemi andmete hoiatamata kustutamine tehase andmetele lähtestamise abil."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Telefoniandmete hoiatuseta kustutamine, lähtestades telefoni tehaseseadetele."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Kasutaja andmete kustutamine"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profiili andmete kustutamine"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Kasutaja andmete kustutamine"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Kustutatakse selle kasutaja andmed sellest tahvelarvutist ilma hoiatamata."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Kustutatakse selle kasutaja andmed sellest Android TV seadmest ilma hoiatamata."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Selle profiili andmete hoiatamata kustutamine teabe ja meelelahutuse süsteemist."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Kustutatakse selle kasutaja andmed sellest telefonist ilma hoiatamata."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Määra seadme globaalne puhverserver"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Määratakse kasutatava seadme üldine puhverserver, kui reegel on lubatud. Üldise puhverserveri saab määrata ainult seadme omanik."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index dc28a1a..d84bdddd 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -676,10 +676,10 @@
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Kontu baten sinkronizazio-ezarpenak aldatzeko baimena ematen die aplikazioei. Adibidez, Jendea aplikazioa kontu batekin sinkronizatzeko erabil daiteke."</string>
<string name="permlab_readSyncStats" msgid="3747407238320105332">"irakurri sinkronizazio-estatistikak"</string>
<string name="permdesc_readSyncStats" msgid="3867809926567379434">"Kontu baten sinkronizazio-estatistikak irakurtzeko baimena ematen dio; besteak beste, sinkronizazio-gertaeren historia eta sinkronizatutako datu kopurua."</string>
- <string name="permlab_sdcardRead" msgid="5791467020950064920">"Irakurri biltegiratze partekatuko edukia"</string>
- <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Biltegiratze partekatuko edukia irakurtzeko baimena ematen die aplikazioei."</string>
- <string name="permlab_sdcardWrite" msgid="4863021819671416668">"aldatu edo ezabatu biltegiratze partekatuko edukia"</string>
- <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Biltegiratze partekatuko edukian idazteko baimena ematen die aplikazioei."</string>
+ <string name="permlab_sdcardRead" msgid="5791467020950064920">"Irakurri biltegi partekatuko edukia"</string>
+ <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Biltegi partekatuko edukia irakurtzeko baimena ematen die aplikazioei."</string>
+ <string name="permlab_sdcardWrite" msgid="4863021819671416668">"aldatu edo ezabatu biltegi partekatuko edukia"</string>
+ <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Biltegi partekatuko edukian idazteko baimena ematen die aplikazioei."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"egin/jaso SIP deiak"</string>
<string name="permdesc_use_sip" msgid="3590270893253204451">"SIP deiak egitea eta jasotzeko baimena ematen die aplikazioei."</string>
<string name="permlab_register_sim_subscription" msgid="1653054249287576161">"erregistratu telekomunikabideekiko SIM konexio berriak"</string>
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gainbegiratu pantaila desblokeatzeko saiakerak"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Kontrolatu pantaila desblokeatzen saiatzean idatzitako pasahitz oker kopurua, eta blokeatu tableta edo ezabatu bere datuak pasahitza gehiegitan oker idazten bada."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Kontrolatu zenbat aldiz idatzi duzun oker pasahitza pantaila desblokeatzen saiatzean, eta blokeatu Android TV gailua edo ezabatu bertako datu guztiak pasahitza gehiegitan idazten baduzu oker."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Kontrolatu zenbatetan idazten duzun pasahitza oker pantaila desblokeatzen saiatzean eta, gehiegitan idazten bada oker, blokeatu informazio- eta aisia-sistema edo ezabatu hango eduki guztia."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Kontrolatu pantaila desblokeatzen saiatzean idatzitako pasahitz oker kopurua, eta blokeatu telefonoa edo ezabatu bere datuak pasahitza gehiegitan oker idazten bada."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Kontrolatu pantaila desblokeatzen saiatzean idatzitako pasahitz oker kopurua, eta blokeatu tableta edo ezabatu erabiltzailearen datuak pasahitza gehiegitan oker idazten bada."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Kontrolatu zenbat aldiz idatzi duzun oker pasahitza pantaila desblokeatzen saiatzean, eta blokeatu Android TV gailua edo ezabatu erabiltzailearen datuak pasahitza gehiegitan idazten baduzu oker."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Kontrolatu zenbatetan idazten duzun pasahitza oker pantaila desblokeatzen saiatzean eta, gehiegitan idazten bada oker, blokeatu informazio- eta aisia-sistema edo ezabatu profil honetako eduki guztia."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Kontrolatu pantaila desblokeatzen saiatzean idatzitako pasahitz oker kopurua, eta blokeatu telefonoa edo ezabatu erabiltzailearen datuak pasahitza gehiegitan oker idazten bada."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Aldatu pantailaren blokeoa"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Aldatu pantailaren blokeoa."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Ezabatu datu guztiak"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ezabatu tabletaren datuak abisatu gabe, jatorrizko datuak berrezarrita."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ezabatu Android TV gailuaren datuak abisatu gabe eta berrezarri jatorrizko datuak."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Berrezarri informazio- eta aisia-sistemako jatorrizko datuak abisatu gabe, bertan zegoen eduki guztia ezabatzeko."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ezabatu telefonoaren datuak abisatu gabe, jatorrizko datuak berrezarrita."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Ezabatu erabiltzailearen datuak"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Ezabatu profileko eduki guztia"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Ezabatu erabiltzailearen datuak"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ezabatu erabiltzaileak tabletan dituen datuak abisatu gabe."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ezabatu erabiltzaileak Android TV gailuan dituen datuak abisatu gabe."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ezabatu informazio- eta aisia-sisteman dagoen profil honetako eduki guztia abisatu gabe."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ezabatu erabiltzaileak telefonoan dituen datuak abisatu gabe."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Ezarri gailuaren proxy orokorra"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Ezarri gailuaren proxy orokorra gidalerroak gaituta dauden bitartean erabiltzeko. Gailuaren jabeak soilik ezar dezake proxy orokorra."</string>
@@ -1530,7 +1535,7 @@
<item quantity="one">Emaitza bat</item>
</plurals>
<string name="action_mode_done" msgid="2536182504764803222">"Eginda"</string>
- <string name="progress_erasing" msgid="6891435992721028004">"Biltegiratze partekatuko eduki guztia ezabatzen…"</string>
+ <string name="progress_erasing" msgid="6891435992721028004">"Biltegi partekatuko eduki guztia ezabatzen…"</string>
<string name="share" msgid="4157615043345227321">"Partekatu"</string>
<string name="find" msgid="5015737188624767706">"Aurkitu"</string>
<string name="websearch" msgid="5624340204512793290">"Web-bilaketa"</string>
@@ -1585,7 +1590,7 @@
<string name="action_menu_overflow_description" msgid="4579536843510088170">"Aukera gehiago"</string>
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="8490227947584914460">"Barneko memoria partekatua"</string>
+ <string name="storage_internal" msgid="8490227947584914460">"Barneko biltegi partekatua"</string>
<string name="storage_sd_card" msgid="3404740277075331881">"SD txartela"</string>
<string name="storage_sd_card_label" msgid="7526153141147470509">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD txartela"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"USB bidezko unitatea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 7c964ae..8f1daa2 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"پایش تلاشهای باز کردن قفل صفحه"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"تعداد گذرواژههای نادرست تایپ شده را هنگام بازکردن قفل صفحه کنترل میکند، و اگر دفعات زیادی گذرواژه نادرست وارد شود رایانهٔ لوحی را قفل میکند و همه دادههای رایانهٔ لوحی را پاک میکند."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"بر تعداد گذرواژههای نادرستی که هنگام بازکردن قفل صفحه تایپ میشود، نظارت میکند، و اگر گذرواژههای نادرست دفعات زیادی تایپ شوند، Android TV را قفل میکند یا همه دادههای آن را پاک میکند."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"بر تعداد گذرواژههای نادرست تایپشده در زمان باز کردن قفل صفحه نظارت میشود و اگر گذرواژههای نادرست دفعات خیلی زیادی تایپ شود، سیستم اطلاعات-سرگرمی قفل میشود یا همه دادههای سیستم اطلاعات-سرگرمی پاک میشود."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"تعداد گذرواژههای نادرست تایپشده را هنگام بازکردن قفل صفحه کنترل میکند و اگر چندین بار گذرواژههای نادرست وارد شود، تلفن را قفل میکند یا همه دادههای تلفن را پاک میکند."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده، نظارت میکند، و اگر تعداد گذرواژههای تایپ شده نادرست بیش از حد بود، رایانه لوحی را قفل میکند یا کلیه دادههای کاربر را پاک میکند."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ میشود، نظارت میکند، و اگر گذرواژههای نادرست دفعات زیادی تایپ شوند، دستگاه Android TV را قفل یا همه دادههای کاربر را پاک میکند."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده نظارت میشود و اگر گذرواژههای نادرست دفعات خیلی زیادی تایپ شود، تلفن قفل میشود یا همه دادههای کاربر پاک میشود."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"بر تعداد گذرواژههای نادرستی که هنگام باز کردن قفل صفحه تایپ شده، نظارت میکند، و اگر تعداد گذرواژههای تایپ شده نادرست بیش از حد بود، تلفن را قفل میکند یا کلیه دادههای کاربر را پاک میکند."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"تغییر قفل صفحه"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"قفل صفحه را تغییر میدهد."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"پاک کردن تمام دادهها"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"با انجام بازنشانی دادههای کارخانه، دادههای رایانهٔ لوحی بدون هشدار پاک میشود."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"دادههای دستگاه Android TV شما، بدون نشان داده شدن هشدار و با انجام بازنشانی دادههای کارخانه، پاک میشود."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"دادههای سیستم اطلاعات-سرگرمی بدون هشدار و با انجام بازنشانی دادههای کارخانه پاک میشود."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"با انجام بازنشانی دادههای کارخانه، دادههای تلفن بدون هشدار پاک میشود."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"پاک کردن دادههای کاربر"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"پاک کردن دادههای نمایه"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"پاک کردن دادههای کاربر"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"دادههای این کاربر را در این رایانه لوحی بدون هشدار پاک میکند."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"بدون نشان داده شدن هشدار، دادههای این کاربر در این دستگاه Android TV پاک میشود."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"دادههای این نمایه در این سیستم اطلاعات-سرگرمی بدوم هشدار پاک میشود."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"دادههای این کاربر را در این تلفن بدون هشدار پاک میکند."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"تنظیم پروکسی جهانی دستگاه"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"پروکسی کلی دستگاه را برای استفاده هنگام فعال بودن این خطمشی تنظیم میکند. تنها مالک دستگاه میتواند پروکسی کلی را تنظیم کند."</string>
@@ -917,7 +922,7 @@
<string name="emergency_calls_only" msgid="3057351206678279851">"فقط تماسهای اضطراری"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"شبکه قفل شد"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"سیم کارت با PUK قفل شده است."</string>
- <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"لطفاً به راهنمای کاربر مراجعه کرده یا با مرکز پشتیبانی از مشتریان تماس بگیرید."</string>
+ <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"لطفاً به «راهنمای کاربر» مراجعه کنید یا با مرکز «مراقبت از مشتریان» تماس بگیرید."</string>
<string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"سیم کارت قفل شد."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"بازگشایی قفل سیم کارت…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. \n\nپساز <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index aba14ae..b63e729 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Tarkkailla näytön avaamisyrityksiä"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Valvoo väärien salasanojen lukumäärää näytön lukituksen poistossa sekä lukitsee tablet-laitteen tai poistaa sen tiedot, jos salasana syötetään väärin liian monta kertaa."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Valvo väärien salasanojen määrää ruudun lukitusta avattaessa ja lukitse Android TV ‑laite tai poista kaikki Android TV ‑laitteen tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Valvoo väärien salasanojen lukumäärää näytön lukituksen poistossa ja lukitsee infotainment-järjestelmän tai poistaa sen kaiken datan, jos väärä salasana syötetään liian monta kertaa."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Valvoo väärien salasanojen lukumäärää näytön lukituksen poistossa ja lukitsee puhelimen tai poistaa sen kaikki tiedot, jos väärä salasana syötetään liian monta kertaa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Valvo väärien salasanojen määrää ruudun lukitusta avattaessa ja lukitse tabletti tai poista kaikki tämän käyttäjän tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Valvo väärien salasanojen määrää ruudun lukitusta avattaessa ja lukitse Android TV ‑laite tai poista kaikki tämän käyttäjän tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Valvoo väärien salasanojen määrää näytön lukituksen poistossa ja lukitsee infotainment-järjestelmän tai poistaa kaiken tämän profiilin datan, jos väärä salasana syötetään liian monta kertaa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Valvo väärien salasanojen määrää ruudun lukitusta avattaessa ja lukitse puhelin tai poista kaikki tämän käyttäjän tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Muuttaa näytön lukituksen"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Muuttaa näytön lukituksen."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Pyyhkiä kaikki tiedot"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Tyhjennä tablet-laitteen tiedot varoituksetta palauttamalla tehdasasetukset."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Tyhjentää Android TV ‑laitteen tiedot ilman varoitusta palauttamalla tehdasasetukset."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Poistaa infotainment-järjestelmän datan ilman varoitusta palauttamalla tehdasasetukset."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Tyhjentää puhelimen tiedot varoituksetta palauttamalla tehdasasetukset."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Pyyhi käyttäjän tiedot"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profiilidatan poistaminen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Pyyhi käyttäjän tiedot"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Pyyhi tämän käyttäjän tiedot tabletista ilman varoitusta."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Tyhjentää tämän käyttäjän tiedot Android TV ‑laitteesta ilman varoitusta."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Poistaa tämän profiilin datan tästä infotainment-järjestelmästä ilman varoitusta."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Pyyhi tämän käyttäjän tiedot puhelimesta ilman varoitusta."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Aseta laitteen yleinen välityspalvelin"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Aseta laitteen yleinen välityspalvelin käyttöön, kun käytäntö on käytössä. Vain laitteen omistaja voi asettaa yleisen välityspalvelimen."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 88d6cd3..cae01af 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -557,10 +557,10 @@
<string name="permdesc_postNotification" msgid="5974977162462877075">"Permet à l\'application d\'afficher les notifications"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"utiliser le matériel biométrique"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permet à l\'application d\'utiliser du matériel biométrique pour l\'authentification"</string>
- <string name="permlab_manageFingerprint" msgid="7432667156322821178">"gérer le matériel d\'empreinte digitale"</string>
+ <string name="permlab_manageFingerprint" msgid="7432667156322821178">"gérer le lecteur d\'empreintes digitales"</string>
<string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Permet à l\'application de faire appel à des méthodes d\'ajout et de suppression de modèles d\'empreinte digitale que vous pouvez utiliser."</string>
- <string name="permlab_useFingerprint" msgid="1001421069766751922">"utiliser le matériel d\'empreinte digitale"</string>
- <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permet à l\'application d\'utiliser du matériel d\'empreinte digitale pour l\'authentification"</string>
+ <string name="permlab_useFingerprint" msgid="1001421069766751922">"utiliser le lecteur d\'empreintes digitales"</string>
+ <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permet à l\'application d\'utiliser le lecteur d\'empreintes digitales pour l\'authentification"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"modifier votre collection de musique"</string>
<string name="permdesc_audioWrite" msgid="8057399517013412431">"Autorise l\'application à modifier votre collection de musique."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"modifier votre collection de vidéos"</string>
@@ -596,7 +596,7 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Matériel d\'empreinte digitale numérique indisponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Lecteur d\'empreintes digitales indisponible."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte digitale"</string>
<string name="fingerprint_error_timeout" msgid="2946635815726054226">"Le temps attribué pour lire l\'empreinte digitale est écoulé. Veuillez réessayer."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale numérique annulée."</string>
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller la tablette ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Surveillez le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouillez votre appareil Android TV ou effacez toutes les données qu\'il contient en cas d\'un nombre trop élevé de tentatives."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Surveillez le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouillez le système d\'infodivertissement ou effacez toutes ses données en cas d\'un nombre trop élevé de tentatives."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouille le téléphone ou efface toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Surveille le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouille la tablette ou efface toutes les données de l\'utilisateur en cas d\'un nombre trop élevé de tentatives."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Surveillez le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouillez votre appareil Android TV ou effacez toutes les données de l\'utilisateur en cas d\'un nombre trop élevé de tentatives."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Surveillez le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouillez le système d\'infodivertissement ou effacez toutes les données de ce profil en cas d\'un nombre trop élevé de tentatives."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Surveille le nombre de mots de passe incorrects entrés lors du déverrouillage de l\'écran et verrouille le téléphone ou efface toutes les données de l\'utilisateur en cas d\'un nombre trop élevé de tentatives."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Modifier le verrouillage de l\'écran"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Modifier le verrouillage de l\'écran."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Effacer toutes les données"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Effacer les données de la tablette sans avertissement, en rétablissant les paramètres par défaut"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Effacez les données de votre appareil Android TV sans avertissement en effectuant une réinitialisation des paramètres d\'usine."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Effacez les données du système d\'infodivertissement sans avertissement en rétablissant les paramètres par défaut."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Effacer les données du téléphone sans avertissement en rétablissant les paramètres par défaut."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Effacer les données de l\'utilisateur"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Effacer les données de profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Effacer les données de l\'utilisateur"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Effacer les données de l\'utilisateur sur cette tablette sans avertissement."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Effacez les données de cet utilisateur sur cet appareil Android TV sans avertissement."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Effacez les données de ce profil sur ce système d\'infodivertissement sans avertissement."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Effacer les données de l\'utilisateur sur ce téléphone sans avertissement."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Définir le serveur mandataire global du mobile"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Indiquer le mandataire global à utiliser pour l\'appareil lorsque la politique est activée. Seul le propriétaire de l\'appareil peut définir le mandataire global."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 17ef670..3955c9f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller la tablette ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Contrôle le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouille votre appareil Android TV ou en efface toutes les données si le nombre maximal de mots de passe incorrects autorisé est dépassé."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le système d\'infoloisirs ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le téléphone ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Contrôlez le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouillez la tablette ou effacez toutes les informations sur l\'utilisateur si le nombre maximal de mots de passe incorrects autorisés est dépassé."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Contrôle le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouille votre appareil Android TV ou efface toutes les données de cet utilisateur si le nombre maximal de mots de passe incorrects autorisé est dépassé."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le système d\'infoloisirs ou effacer toutes les données de ce profil si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Contrôlez le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouillez le téléphone ou effacez toutes les informations sur l\'utilisateur si le nombre maximal de mots de passe incorrects autorisés est dépassé."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Modifier le verrouillage de l\'écran"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Modifier le verrouillage de l\'écran"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Effacer toutes les données"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Effacer les données de la tablette sans avertissement, en rétablissant la configuration d\'usine"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Efface les données de votre appareil Android TV sans avertissement en rétablissant la configuration d\'usine."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Effacer les données du système d\'infoloisirs sans avertissement en rétablissant la configuration d\'usine."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Effacer les données du téléphone sans avertissement, en rétablissant la configuration d\'usine"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Effacer les informations sur l\'utilisateur"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Effacer les données du profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Effacer les informations sur l\'utilisateur"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Effacer les informations sur cet utilisateur de cette tablette sans avertissement"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Efface les données concernant cet utilisateur de cet appareil Android TV sans avertissement."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Effacer les données de ce profil sur ce système d\'infoloisirs sans avertissement."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Effacer les informations sur cet utilisateur de ce téléphone sans avertissement"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Définir le proxy global du mobile"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Indiquer le proxy global à utiliser pour l\'appareil lorsque la règle est activée. Seul le propriétaire de l\'appareil peut définir le proxy global."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index daa95d1..c07287a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Controlar os intentos de desbloqueo da pantalla"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Supervisa o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquea a tableta ou borra todos os datos da tableta se se escriben demasiados contrasinais incorrectos."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Controla o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquea o dispositivo Android TV ou borra todos os datos do dispositivo se se escriben demasiados contrasinais incorrectos."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Supervisa o número de contrasinais incorrectos que se escriben ao desbloquear a pantalla e bloquea o sistema de información e entretemento ou borra todos os seus datos se se meten demasiados incorrectos."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Supervisa o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquea o teléfono ou borra todos os datos do teléfono se se escriben demasiados contrasinais incorrectos."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Supervisar o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquear a tableta ou borrar todos os datos do usuario se se escriben demasiados contrasinais incorrectos."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Controla o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquea o dispositivo Android TV ou borra todos os datos deste usuario se se escriben demasiados contrasinais incorrectos."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Supervisa o número de contrasinais incorrectos que se escriben ao desbloquear a pantalla e bloquea o sistema de información e entretemento ou borra todos os datos deste perfil se se meten demasiados incorrectos."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Supervisar o número de contrasinais incorrectos escritos ao desbloquear a pantalla e bloquear o teléfono ou borrar todos os datos do usuario se se escriben demasiados contrasinais incorrectos."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Cambiar o bloqueo da pantalla"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Cambia o bloqueo da pantalla."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Borrar todos os datos"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Borra os datos da tableta sen previo aviso mediante a realización dun restablecemento dos datos de fábrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Borra a información do dispositivo Android TV sen avisar e realiza un restablecemento dos datos de fábrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Borra os datos do sistema de información e entretemento sen previo aviso a través do restablecemento dos datos de fábrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Borra os datos do teléfono sen previo aviso mediante a realización dun restablecemento dos datos de fábrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Borrar os datos do usuario"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Borrar os datos do perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Borrar os datos do usuario"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Borra os datos deste usuario nesta tableta sen previo aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Borra os datos deste usuario neste dispositivo Android TV sen avisar."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Borra os datos deste perfil do sistema de información e entretemento sen previo aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Borra os datos deste usuario neste teléfono sen previo aviso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Establecer o proxy global do dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Configura o proxy global do dispositivo que se debe usar cando a política está activada. Só o propietario do dispositivo pode configurar o proxy global."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 3be53ab..5528954 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"સ્ક્રીનને અનલૉક કરવાના પ્રયત્નોનું નિયમન કરો"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ટેબ્લેટને લૉક કરો અથવા ટેબ્લેટનો તમામ ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"સ્ક્રીનને અનલૉક કરતી વખતે ટાઇપ કરેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ ટાઇપ કર્યા હોય, તો તમારા Android TV ડિવાઇસના ડેટાને લૉક કરો અથવા આ વપરાશકર્તાનો બધો ડેટા કાઢી નાખો."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ લખ્યા હોય તો ઇન્ફોટેનમેન્ટ સિસ્ટમને લૉક કરો અથવા ઇન્ફોટેનમેન્ટ સિસ્ટમનો બધો ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ફોનને લૉક કરો અથવા ફોનનો તમામ ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ટેબ્લેટને લૉક કરો અથવા આ વપરાશકર્તાનો તમામ ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"સ્ક્રીનને અનલૉક કરતી વખતે ટાઇપ કરેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ ટાઇપ કર્યા હોય તો તમારા Android TV ડિવાઇસને લૉક કરો અથવા આ વપરાશકર્તાનો બધો ડેટા કાઢી નાખો."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ લખ્યા હોય તો ઇન્ફોટેનમેન્ટ સિસ્ટમને લૉક કરો અથવા આ પ્રોફાઇલનો બધો ડેટા કાઢી નાખો."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"સ્ક્રીનને અનલૉક કરતી વખતે લખેલા ખોટા પાસવર્ડ્સની સંખ્યાને મૉનિટર કરો અને જો ઘણા બધા ખોટા પાસવર્ડ્સ લખ્યાં છે તો ફોનને લૉક કરો અથવા આ વપરાશકર્તાનો તમામ ડેટા કાઢી નાખો."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"સ્ક્રીન લૉક બદલો"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"સ્ક્રીન લૉક બદલો."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"બધો ડેટા કાઢી નાખો"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ફેક્ટરી ડેટા ફરીથી સેટ કરોને કરીને ચેતવણી વિના ટેબ્લેટનો ડેટા કાઢી નાખો."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ફેક્ટરી ડેટા રીસેટ કરીને ચેતવણી વિના તમારા Android TV ડિવાઇસનો ડેટા કાઢી નાખો."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ફેક્ટરી ડેટા રીસેટ કરીને કોઈ ચેતવણી વિના જ ઇન્ફોટેનમેન્ટ સિસ્ટમનો ડેટા કાઢી નાખો."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ફેક્ટરી ડેટા ફરીથી સેટ કરોને કરીને ચેતવણી વિના ફોનનો ડેટા કાઢી નાખો."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"વપરાશકર્તા ડેટા કાઢી નાખો"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"પ્રોફાઇલનો ડેટા કાઢી નાખો"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"વપરાશકર્તા ડેટા કાઢી નાખો"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ચેતવણી વિના આ ટેબ્લેટ પરનો આ વપરાશકર્તાનો ડેટા કાઢી નાખો."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ચેતવણી વિના આ Android TV ડિવાઇસ પર રહેલો આ વપરાશકર્તાનો ડેટા કાઢી નાખો."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"કોઈ ચેતવણી વિના જ આ ઇન્ફોટેનમેન્ટ સિસ્ટમ પર રહેલો આ પ્રોફાઇલનો ડેટા કાઢી નાખો."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ચેતવણી વિના આ ફોન પરનો આ વપરાશકર્તાનો ડેટા કાઢી નાખો."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ઉપકરણ વૈશ્વિક પ્રોક્સી સેટ કરો"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"પૉલિસી ચાલુ હોય તે વખતે ઉપયોગ કરવા માટેના ડિવાઇસ વૈશ્વિક પ્રોક્સીને સેટ કરો. ફક્ત ડિવાઇસના માલિક વૈશ્વિક પ્રોક્સી સેટ કરી શકે છે."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 7ec61f9..cb6de5c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रीन अनलॉक करने के की कोशिशों पर नज़र रखना"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रीन को अनलॉक करते समय गलत लिखे गए पासवर्ड की संख्या पर निगरानी करें, और बहुत ज़्यादा बार गलत पासवर्ड लिखे जाने पर टैबलेट लॉक करें या टैबलेट का संपूर्ण डेटा मिटाएं."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने Android TV डिवाइस को तुरंत लॉक करें या इसका सभी डेटा मिटाएं."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो सूचना और मनोरंजन की सुविधा देने वाले डिवाइस को लॉक करें या इस डिवाइस का सारा डेटा मिटाएं."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रीन को अनलॉक करते समय जितनी बार गलत पासवर्ड लिखा गया है, उसकी संख्या पर नज़र रखना और अगर बहुत बार गलत पासवर्ड डाले गए हैं, तो फ़ोन को लॉक कर देना या फ़ोन का सारा डेटा मिटा देना."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रीन का लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार ज़्यादा पासवर्ड लिखे जाते हैं तो टैबलेट को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने Android TV डिवाइस को तुरंत लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटाएं."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो सूचना और मनोरंजन की सुविधा देने वाले डिवाइस को लॉक करें या इस प्रोफ़ाइल का सारा डेटा मिटाएं."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"स्क्रीनका लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार गलत पासवर्ड लिखा जाता है तो फ़ोन को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"स्क्रीन लॉक बदलना"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"इससे स्क्रीन लॉक बदला जाता है."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"सारा डेटा मिटाना"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"फ़ैक्टरी डेटा रीसेट करके चेतावनी दिए बिना फ़ोन का डेटा मिटाना."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ़ैक्ट्री डेटा रीसेट करके अपने Android TV डिवाइस का डेटा बिना चेतावनी दिए मिटाएं."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"फ़ैक्ट्री डेटा रीसेट करके, बिना किसी चेतावनी के सूचना और मनोरंजन की सुविधा देने वाले डिवाइस में सेव डेटा को हमेशा के लिए मिटाएं."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"इससे फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना फ़ोन का डेटा मिट जाता है."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"उपयोगकर्ता डेटा मिटाएं"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफ़ाइल का डेटा मिटाना"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"उपयोगकर्ता डेटा मिटाएं"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"इस टैबलेट पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"बिना चेतावनी दिए, इस Android TV डिवाइस से उपयोगकर्ता का डेटा मिटाएं."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"बिना किसी चेतावनी के, सूचना और मनोरंजन की सुविधा देने वाले डिवाइस में मौजूद इस प्रोफ़ाइल का डेटा मिटाएं."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"इस फ़ोन पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"डिवाइस वैश्विक प्रॉक्सी सेट करें"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"नीति चालू होने के दौरान इस्तेमाल करने के लिए डिवाइस ग्लोबल प्रॉक्सी सेट करें. केवल डिवाइस का मालिक ही ग्लोबल प्रॉक्सी सेट कर सकता है."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 3387fa0..1dc2517 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -740,9 +740,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadziri pokušaje otključavanja zaslona"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Nadziri broj netočnih zaporki unesenih pri otključavanju zaslona i zaključaj tabletno računalo ili izbriši sve podatke na njemu ako je uneseno previše netočnih zaporki."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava Android TV uređaj ili s njega briše sve podatke ako se unese previše netočnih zaporki."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava sustav za informiranje i zabavu ili briše sve njegove podatke ako se unese previše netočnih zaporki."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Nadzire broj netočno unesenih zaporki pri otključavanju zaslona i zaključava telefon ili briše sve podatke na telefonu ako je uneseno previše netočnih zaporki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava tablet ili briše sve podatke korisnika ako se unese previše netočnih zaporki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava Android TV uređaj ili briše sve podatke korisnika ako se unese previše netočnih zaporki."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava sustav za informiranje i zabavu ili briše sve podatke profila ako se unese previše netočnih zaporki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Prati broj netočnih zaporki unesenih prilikom otključavanja zaslona i zaključava telefon ili briše sve podatke korisnika ako se unese previše netočnih zaporki."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Mijenjanje zaporke za zaključavanje"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Mijenja se zaporka za zaključavanje zaslona."</string>
@@ -751,10 +753,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje svih podataka"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Vraćanjem u tvorničko stanje izbriši podatke tabletnog računala bez upozorenja."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Podatke Android TV uređaja izbrišite bez upozorenja vraćanjem uređaja na tvorničke postavke."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Briše podatke sustava za informiranje i zabavu bez upozorenja vraćanjem na tvorničko stanje."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Vraćanjem na tvorničke postavke brišu se podaci s telefona bez upozorenja."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Izbriši podatke korisnika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Izbriši podatke profila"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Izbriši podatke korisnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Briše podatke korisnika na ovom tabletu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Briše podatke korisnika na Android TV uređaju bez upozorenja."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Briše podatke profila na ovom sustavu za informiranje i zabavu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Briše podatke korisnika na ovom telefonu bez upozorenja."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"postavi globalni proxy uređaja"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Postavlja globalni proxy za uređaj koji će se upotrebljavati dok je pravilo omogućeno. Samo vlasnik uređaja može postaviti globalni proxy."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 22de5eb..34f9963 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Képernyőzár-feloldási kísérletek figyelése"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Megfigyeli a képernyő feloldásakor helytelenül beírt jelszavak számát, és túl sok hibásan beírt jelszó esetén lezárja a táblagépet, vagy törli a táblagép összes adatát."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"A képernyő feloldásához megadott helytelen jelszavak számának figyelése, és az Android TV eszköz zárolása vagy az Android TV eszköz összes adatának törlése túl sok helytelen jelszó után."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Megfigyeli a képernyő feloldásakor helytelenül beírt jelszavak számát, és túl sok hibásan beírt jelszó esetén lezárja az infotainmentrendszert, vagy törli az infotainmentrendszer összes adatát."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Megfigyeli a képernyő feloldásakor helytelenül beírt jelszavak számát, és túl sok hibásan beírt jelszó esetén lezárja a telefont, vagy törli a telefon összes adatát."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"A képernyőzárolás során megadott helytelen jelszavak számának figyelése, és a táblagép zárolása vagy a felhasználó összes adatának törlése abban az esetben, ha túl sokszor adtak meg helytelen jelszót."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"A képernyő feloldásához megadott helytelen jelszavak számának figyelése, és az Android TV eszköz zárolása vagy ezen felhasználó összes adatának törlése túl sok helytelen jelszó után."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"A képernyőzárolás során megadott helytelen jelszavak számának figyelése, és az infotainmentrendszer zárolása vagy a profil összes adatának törlése abban az esetben, ha túl sokszor adtak meg helytelen jelszót."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"A képernyőzárolás során megadott helytelen jelszavak számának figyelése, és a telefon zárolása vagy a felhasználó összes adatának törlése abban az esetben, ha túl sokszor adtak meg helytelen jelszót."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"A képernyőzár módosítása"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"A képernyőzár módosítása."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Minden adat törlése"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Figyelmeztetés nélkül törli a táblagép adatait, visszaállítva a gyári adatokat."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Az Android TV eszköz adatainak figyelmeztetés nélküli törlése a gyári adatok visszaállításával."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Az infotainmentrendszer adatainak törlése a gyári alapbeállítások visszaállításával anélkül, hogy figyelmeztetés jelenne meg erről."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Figyelmeztetés nélkül törli a telefon összes adatát, visszaállítva a gyári adatokat."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"A felhasználói adatok törlése"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profiladatok törlése"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"A felhasználói adatok törlése"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"A felhasználó összes adatának törlése erről a táblagépről figyelmeztetés nélkül."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"A felhasználó összes adatának figyelmeztetés nélküli törlése erről az Android TV eszközről."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"A profil adatainak törlése erről az infotainmentrendszerről figyelmeztetés nélkül."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"A felhasználó összes adatának törlése erről a telefonról figyelmeztetés nélkül."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Az eszköz globális proxyjának beállítása"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Az eszköz globális proxyja lesz használatban, amíg a házirend engedélyezve van. A globális proxyt csak az eszköz tulajdonosa állíthatja be."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index c79d4f5..03f9e1d 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Վերահսկել էկրանի ապակողպման փորձերը"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Վերահսկել սխալ գաղտնաբառերի թիվը, որոնք մուտքագրվել են էկրանն ապակողպելիս, և կողպել պլանշետը կամ ջնջել պլանշետի բոլոր տվյալները, եթե մուտքագրվել են չափից շատ սխալ գաղտնաբառեր:"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Գրանցել էկրանի ապակողպման համար մուտքագրվող սխալ գաղտնաբառերի թիվը և կողպել Android TV սարքը կամ ջնջել սարքի բոլոր տվյալները՝ չափից ավելի շատ սխալ գաղտնաբառեր մուտքագրելու դեպքում:"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Վերահսկել սխալ գաղտնաբառերի թիվը, որոնք մուտքագրվել են էկրանը ապակողպելիս, և կողպել տեղեկատվաժամանցային համակարգը կամ ջնջել բոլոր տվյալները, եթե չափից ավելի սխալ գաղտնաբառեր են մուտքագրվել։"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Վերահսկել սխալ գաղտնաբառերի թիվը, որոնք մուտքագրվել են էկրանն ապակողպելիս, և կողպել հեռախոսը կամ ջնջել հեռախոսի բոլոր տվյալները, եթե մուտքագրվել են չափից շատ սխալ գաղտնաբառեր:"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Կառավարել էկրանն ապակողպելիս մուտքագրվող սխալ գաղտնաբառերի թիվը և կողպել պլանշետը կամ ջնջել այս օգտատիրոջ բոլոր տվյալները չափից ավելի սխալ գաղտնաբառեր մուտքագրելու դեպքում:"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Գրանցել էկրանի ապակողպման համար մուտքագրվող սխալ գաղտնաբառերի թիվը և կողպել Android TV սարքը կամ ջնջել այս օգտատիրոջ բոլոր տվյալները՝ չափից ավելի շատ սխալ գաղտնաբառեր մուտքագրելու դեպքում:"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Վերահսկել սխալ գաղտնաբառերի թիվը, որոնք մուտքագրվել են էկրանը ապակողպելիս, և կողպել տեղեկատվաժամանցային համակարգը կամ ջնջել այս պրոֆիլի բոլոր տվյալները, եթե չափից ավելի սխալ գաղտնաբառեր են մուտքագրվել։"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Կառավարել էկրանն ապակողպելիս մուտքագրվող սխալ գաղտնաբառերի թիվը և կողպել հեռախոսը կամ ջնջել այս օգտատիրոջ բոլոր տվյալները չափից ավելի սխալ գաղտնաբառեր մուտքագրելու դեպքում:"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Փոխել էկրանի կողպման գաղտնաբառը"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Փոխել էկրանի կողպման գաղտնաբառը:"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Ջնջել բոլոր տվյալները"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ջնջել պլանշետի տվյալներն առանց նախազգուշացման` կատարելով գործարանային տվյալների վերակայում:"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ջնջել Android TV սարքի տվյալներն առանց զգուշացման՝ վերականգնելով գործարանային կարգավորումները:"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ջնջել տեղեկատվաժամանցային համակարգի տվյալները առանց զգուշացման՝ վերականգնելով գործարանային կարգավորումները։"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ջնջել հեռախոսի տվյալներն առանց նախազգուշացման` կատարելով գործարանային տվյալների վերակայում:"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Ջնջել օգտատիրոջ տվյալները"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Պրոֆիլի տվյալների ջնջում"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Ջնջել օգտատիրոջ տվյալները"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ջնջել այս օգտատիրոջ տվյալներն այս պլանշետում առանց նախազգուշացման:"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ջնջել այս օգտատիրոջ տվյալներն Android TV սարքում առանց նախազգուշացման:"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ջնջել տեղեկատվաժամանցային համակարգի այս պրոֆիլի տվյալները առանց զգուշացման։"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ջնջել այս օգտատիրոջ տվյալներն այս հեռախոսում առանց նախազգուշացման:"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Կարգավորել սարքի համաշխարհային պրոքսին"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Կարգավորել, որ սարքի համընդհանուր պրոքսի-սերվերն օգտագործվի, երբ քաղաքականությունը միացված է: Միայն սարքի սեփականատերը կարող է կարգավորել համընդհանուր պրոքսի-սերվերը:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 080f1e1..db97591 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau upaya pembukaan kunci layar"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci tablet atau menghapus semua data tablet jika terjadi terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci perangkat Android TV atau menghapus semua data perangkat Android TV jika terlalu banyak sandi salah diketikkan."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci sistem infotainmen atau menghapus semua data sistem infotainmen jika terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci ponsel atau menghapus semua data ponsel jika terjadi terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci tablet atau menghapus semua data pengguna ini jika terjadi terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci perangkat Android TV atau menghapus semua data pengguna ini jika terlalu banyak sandi salah diketikkan."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci sistem infotainmen atau menghapus semua data profil ini jika terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Memantau berapa kali sandi yang dimasukkan salah saat ingin membuka kunci layar, dan mengunci ponsel atau menghapus semua data pengguna ini jika terjadi terlalu banyak kesalahan memasukkan sandi."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Mengubah kunci layar"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Mengubah kunci layar."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Menghapus semua data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Menghapus data tablet tanpa peringatan dengan mereset ke setelan pabrik."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Menghapus data perangkat Android TV tanpa peringatan dengan melakukan reset ke setelan pabrik."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Menghapus data sistem infotainmen tanpa peringatan dengan melakukan reset ke setelan pabrik."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Menghapus data ponsel tanpa peringatan dengan melakukan reset ke setelan pabrik."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Menghapus data pengguna"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Hapus data profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Menghapus data pengguna"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Menghapus data pengguna ini di tablet ini tanpa peringatan."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Menghapus data pengguna ini di perangkat Android TV ini tanpa peringatan."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Menghapus data profil ini di sistem infotainmen ini tanpa peringatan."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Menghapus data pengguna ini di ponsel ini tanpa peringatan."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Setel proxy global perangkat"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Menyetel proxy global perangkat yang akan digunakan jika kebijakan diaktifkan. Hanya pemilik perangkat yang dapat menyetel proxy global."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 78e01c9..d70a66b 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Fylgjast með tilraunum til að taka skjáinn úr lás"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa spjaldtölvunni eða eyða öllum gögnum hennar ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa Android TV tækinu eða eyða öllum gögnum tækisins ef rangt aðgangsorð er fært inn of oft."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa upplýsinga- og afþreyingarkerfinu eða eyða öllum gögnum upplýsinga- og afþreyingarkerfisins ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa símanum eða eyða öllum gögnum hans ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa spjaldtölvunni eða eyða öllum gögnum viðkomandi notanda ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa Android TV eða eyða öllum gögnum viðkomandi notanda ef rangt aðgangsorð er fært inn of oft."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa upplýsinga- og afþreyingarkerfinu eða eyða öllum gögnum þessa prófíls ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Fylgjast með fjölda rangra innskráningartilrauna með aðgangsorði þegar skjárinn er tekinn úr lás og læsa símanum eða eyða öllum gögnum viðkomandi notanda ef rangt aðgangsorð er fært inn of oft."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Breyta skjálásnum"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Breyta skjálásnum."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Eyða öllum gögnum"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Eyða gögnum spjaldtölvunnar fyrirvaralaust með núllstillingu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Eyða gögnum Android TV tækisins án viðvörunar með því að núllstilla það."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Eyða gögnum upplýsinga- og afþreyingarkerfisins án viðvörunar með núllstillingu."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Eyða gögnum símans fyrirvaralaust með núllstillingu."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Eyða gögnum notanda"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Eyða prófílgögnum"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Eyða gögnum notanda"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Eyða gögnum viðkomandi notanda í þessari spjaldtölvu án viðvörunar."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Eyða gögnum þessa notanda úr þessu Android TV tæki án viðvörunar."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Eyða gögnum þessa prófíls í þessu upplýsinga- og afþreyingarkerfi án viðvörunar."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Eyða gögnum viðkomandi notanda í þessum síma án viðvörunar."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Stilla altækan proxy-þjón fyrir tækið"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Velja altækan proxy-þjón tækisins sem nota á þegar stefnan er virk. Aðeins eigandi tækisins getur valið altækan proxy-þjón."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 548d4ad2..91df4bd 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorare tentativi di sblocco dello schermo"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il tablet o cancella tutti i dati del tablet se vengono digitate troppe password errate."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Consente di monitorare il numero di password errate digitate durante lo sblocco dello schermo e di bloccare il dispositivo Android TV o cancellare tutti i dati del dispositivo se vengono digitate troppe password errate."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il sistema di infotainment o cancella tutti i dati del sistema se vengono digitate troppe password errate."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il telefono o cancella tutti i dati del telefono se vengono digitate troppe password errate."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il tablet o resetta tutti i dati dell\'utente se è stato raggiunto il limite massimo consentito."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Consente di monitorare il numero di password errate digitate durante lo sblocco dello schermo e di bloccare il dispositivo Android TV o cancellare tutti i dati dell\'utente se vengono digitate troppe password errate."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il sistema di infotainment o cancella tutti i dati di questo profilo se vengono digitate troppe password errate."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitora il numero di password errate digitate durante lo sblocco dello schermo e blocca il telefono o resetta tutti i dati dell\'utente se è stato raggiunto il limite massimo consentito."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Modificare il blocco schermo"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Modifica il blocco schermo."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Cancellare tutti i dati"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Cancella i dati del tablet senza preavviso eseguendo un ripristino dati di fabbrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Consente di cancellare i dati del dispositivo Android TV senza preavviso eseguendo un ripristino dei dati di fabbrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Cancella i dati del sistema di infotainment senza preavviso eseguendo un ripristino dei dati di fabbrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Cancella i dati del telefono senza preavviso eseguendo un ripristino dei dati di fabbrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Resettare i dati dell\'utente"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Cancella dati del profilo"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Resettare i dati dell\'utente"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Resetta i dati dell\'utente sul tablet senza preavviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Consente di cancellare i dati dell\'utente su questo dispositivo Android TV senza preavviso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Cancella i dati di questo profilo su questo sistema di infotainment senza preavviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Resetta i dati dell\'utente sul telefono senza preavviso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Impostare il proxy globale del dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Imposta il proxy globale del dispositivo in modo da utilizzarlo mentre la norma è attiva. Il proxy globale può essere impostato solo dal proprietario del dispositivo."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 0c5a4a8..a16c682 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"מעקב אחר ניסיונות לביטול של נעילת המסך"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ניהול מעקב אחר מספר הסיסמאות השגויות שמוקלדות במהלך ביטול נעילת המסך, וביצוע נעילה של הטאבלט, או מחיקה של כל נתוני הטאבלט, אם מוקלדות יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"מעקב אחר מספר הסיסמאות השגויות שהוזנו בעת ביטול נעילת המסך, כמו גם נעילה של מכשיר ה-Android TV או מחיקה של כל נתוני מכשיר ה-Android TV אם הוזנו יותר מדי סיסמאות שגויות."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"מעקב אחר מספר הסיסמאות השגויות שהוזנו במהלך ביטול נעילת המסך, נעילת מערכת המידע והבידור או מחיקה של כל נתוני מערכת המידע והבידור, אם הוזנו יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"מעקב אחר מספר הסיסמאות השגויות שהוקלדו במהלך ביטול נעילת המסך, וביצוע נעילה של הטלפון או מחיקה של כל נתוני הטלפון אם הוקלדו יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"מעקב אחר מספר הסיסמאות השגויות שהוזנו בעת ביטול נעילת המסך, כמו גם נעילת הטאבלט או מחיקה של כל נתוני המשתמש הזה אם הוזנו יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"מעקב אחר מספר הסיסמאות השגויות שהוזנו במהלך ביטול נעילת המסך, כמו גם נעילה של מכשיר ה-Android TV או מחיקה של כל נתוני המשתמש הזה אם הוזנו יותר מדי סיסמאות שגויות."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"מעקב אחר מספר הסיסמאות השגויות שהוזנו במהלך ביטול נעילת המסך, נעילת מערכת המידע והבידור או מחיקה של כל נתוני הפרופיל הזה, אם הוזנו יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"מעקב אחר מספר הסיסמאות השגויות שהוזנו בעת ביטול נעילת המסך, כמו גם נעילת הטלפון או מחיקה של כל נתוני המשתמש הזה אם הוזנו יותר מדי סיסמאות שגויות."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"שינוי נעילת המסך"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"שינוי של נעילת המסך."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"מחיקת כל הנתונים"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"מחיקה של נתוני הטאבלט ללא אזהרה, באמצעות איפוס לנתוני היצרן."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"מחיקה ללא אזהרה של נתוני מכשיר ה-Android TV באמצעות איפוס לנתוני היצרן."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"מחיקה של נתוני מערכת המידע והבידור, ללא אזהרה, על ידי ביצוע איפוס לנתוני היצרן."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"מחיקה של נתוני הטלפון, ללא אזהרה, על ידי ביצוע איפוס לנתוני היצרן."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"מחיקת נתוני משתמש"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"מחיקת נתוני הפרופיל"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"מחיקת נתוני משתמש"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"מחיקה של נתוני המשתמש הזה בטאבלט, ללא אזהרה."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"מחיקה של נתוני המשתמש הזה במכשיר ה-Android TV, ללא אזהרה."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"מחיקה של נתוני הפרופיל במערכת המידע והבידור הזו, ללא אזהרה."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"מחיקה של נתוני המשתמש הזה בטלפון, ללא אזהרה."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"הגדרה של שרת ה-Proxy הכללי של המכשיר"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"הגדרה של שרת ה-proxy הגלובלי שבו ייעשה שימוש כשהמדיניות פועלת. רק לבעלים של המכשיר יש אפשרות להגדיר את שרת ה-proxy הגלובלי."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 911b6d2..ff7a868 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"画面ロック解除試行の監視"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はタブレットをロックするかタブレットのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合は Android TV デバイスをロックするか Android TV デバイスのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はインフォテインメント システムをロックするかインフォテインメント システムのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はモバイルデバイスをロックするかモバイルデバイスのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合はタブレットをロックするかこのユーザーのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合は Android TV デバイスをロックするかこのユーザーのデータをすべて消去します。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"画面のロック解除に正しくないパスワードを入力した回数を監視し、回数が多すぎる場合はインフォテインメント システムをロックするかこのプロファイルのデータをすべて消去します。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"画面のロック解除の際に入力したパスワードが間違っていた回数を監視し、回数が多すぎる場合はスマートフォンをロックするかこのユーザーのデータをすべて消去します。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"画面ロックの変更"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"画面ロックを変更します。"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"すべてのデータを消去"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"警告せずにタブレットを初期化して内部のデータを消去します。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"警告せずに出荷時設定へのリセットを実行して Android TV デバイスのデータを消去します。"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"警告せずにインフォテインメント システムを初期化して内部のデータを消去します。"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"警告せずにデバイスを初期化して内部のデータを消去します。"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ユーザーデータの消去"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"プロファイル データの消去"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ユーザーデータの消去"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"このタブレットのこのユーザーのデータを警告なく消去します。"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"この Android TV デバイスでこのユーザーのデータを警告なく消去します。"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"警告せずにこのインフォテインメント システムにあるこのプロファイルのデータを消去します。"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"このスマートフォンのこのユーザーのデータを警告なく消去します。"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"デバイスのグローバルプロキシを設定"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ポリシーが有効になっている場合はデバイスのグローバルプロキシが使用されるように設定します。グローバルプロキシを設定できるのはデバイスの所有者だけです。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index a4e0123..dbc7bf59 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ეკრანის განბლოკვის მცდელობების მონიტორინგი"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ეკრანის განბლოკვისთვის არასწორად აკრეფილი პაროლების რაოდენობის მონიტორინგი. ტაბლეტის დაბლოკვა ან მასზე არსებული ყველა მონაცემის წაშლა ძალიან ბევრჯერ არასწორი პაროლის შეყვანის შემთხვევაში."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ეკრანის განბლოკვისას არასწორი პაროლების შეყვანილი რაოდენობის მონიტორინგი და არასწორი პაროლის მეტისმეტად ბევრჯერ შეყვანის შემთხვევაში, Android TV მოწყობილობის დაბლოკვა ან Android TV მოწყობილობის მთელი ინფორმაციის ამოშლა."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ეკრანის განბლოკვისას გააკონტროლეთ პაროლების არასწორად შეყვანის რაოდენობა და დაბლოკეთ გართობის/საინფორმაციო სისტემა ან წაშალეთ მისი ყველა მონაცემი იმ შემთხვევაში, თუ ძალიან ბევრჯერ მოხდა არასწორი პაროლის შეყვანა."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ეკრანის განბლოკვისთვის არასწორად აკრეფილი პაროლების რაოდენობის მონიტორინგი. ტელეფონის დაბლოკვა ან მასზე არსებული ყველა მონაცემის წაშლა ძალიან ბევრჯერ არასწორი პაროლის შეყვანის შემთხვევაში."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ეკრანის განბლოკვისას არასწორი პაროლების შეყვანილი რაოდენობის მონიტორინგი და ტაბლეტის დაბლოკვა ან მრავლალჯერ არასწორი პაროლის შეყვანის შემთხვევაში ამ მომხმარებლის მთელი ინფორმაციის წაშლა."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ეკრანის განბლოკვისას არასწორი პაროლების შეყვანილი რაოდენობის მონიტორინგი და არასწორი პაროლის მეტისმეტად ბევრჯერ შეყვანის შემთხვევაში, Android TV მოწყობილობის დაბლოკვა ან ამ მომხმარებლის მთელი ინფორმაციის ამოშლა."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ეკრანის განბლოკვისას გააკონტროლეთ პაროლების არასწორად შეყვანის რაოდენობა და დაბლოკეთ გართობის/საინფორმაციო სისტემა ან წაშალეთ ამ პროფილის ყველა მონაცემი იმ შემთხვევაში, თუ ძალიან ბევრჯერ მოხდა არასწორი პაროლის შეყვანა."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ეკრანის განბლოკვისას არასწორი პაროლების შეყვანილი რაოდენობის მონიტორინგი და ტელეფონის დაბლოკვა ან მრავლალჯერ არასწორი პაროლის შეყვანის შემთხვევაში ამ მომხმარებლის მთელი ინფორმაციის წაშლა."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ეკრანის დაბლოკვის შეცვლა"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ეკრანის დაბლოკვის შეცვლა"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ყველა მონაცემის წაშლა"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ტაბლეტის მონაცემების გაუფრთხილებლად წაშლა, ქარხნული მონაცემების აღდგენით"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"თქვენი Android TV მოწყობილობის მონაცემების გაუფრთხილებლად ამოშლა ქარხნული მონაცემების აღდგენის გზით."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ქარხნული მონაცემების აღდგენით წაშალეთ გართობის/საინფორმაციო სისტემის მონაცემები გაფრთხილების გარეშე."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ტელეფონის მონაცემების გაუფრთხილებლად წაშლა, ქარხნული მონაცემების აღდგენით"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"მომხმარებლის მონაცემების წაშლა"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"პროფილის მონაცემების წაშლა"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"მომხმარებლის მონაცემების წაშლა"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ამ მომხმარებლის მონაცემების გაუფრთხილებელი წაშლა ამ ტაბლეტზე."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"მომხმარებლის მონაცემების გაუფრთხილებლად ამოშლა Android TV მოწყობილობიდან."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ქარხნული მონაცემების აღდგენით წაშალეთ ამ პროფილის მონაცემები ამ გართობ/საინფორმაციო სისტემაში გაფრთხილების გარეშე."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ამ მომხმარებლის მონაცემების გაუფრთხილებელი წაშლა ამ ტელეფონზე."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"მოწყობილობის გლობალური პროქსის დაყენება"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ჩართული პოლიტიკის დროს მოწყობილობის გლობალური პროქსის დაყენება. მხოლოდ მოწყობილობის მფლობელს შეუძლია გლობალური პროქსის დაყენება."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 48bc9cb..c1d421f 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран құлпын ашу әркеттерін бақылау"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Экран бекітпесін ашқан кезде терілген қате құпия сөздердің санын бақылау және планшетті бекіту немесе тым көп қате құпия сөздер терілген болса, планшеттің бүкіл деректерін өшіру."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Экранның құлпын ашу кезінде қате енгізілген құпия сөздердің санын бақылау, құпия сөз тым көп қате енгізілген жағдайда, Android TV құрылғысын құлыптау және Android TV құрылғыңыздың барлық деректерінен тазарту."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Экран құлпын ашқан кезде, терілген қате құпия сөздердің саны бақыланады, сондай-ақ құпия сөздер бірнеше рет қате терілсе, ақпараттық-сауықтық жүйе құлыпталады немесе оның барлық дерегі жойылады."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Экран бекітпесін ашқан кезде терілген қате құпия сөздердің санын бақылау және телефонды бекіту немесе тым көп қате құпия сөздер терілген болса, телефонның бүкіл деректерін өшіру."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Экран бекітпесін ашқанда терілген қате құпия сөздердің санын бақылау және тым көп қате құпия сөздер терілсе, планшетті бекіту немесе осы пайдаланушының барлық деректерін өшіру."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Экранның құлпын ашу кезінде қате енгізілген құпия сөздердің санын бақылау, құпия сөз тым көп қате енгізілген жағдайда, Android TV құрылғысын құлыптау және барлық пайдаланушы деректерінен тазарту."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Экран құлпын ашқан кезде, терілген қате құпия сөздердің саны бақыланады, сондай-ақ құпия сөздер бірнеше рет қате терілсе, ақпараттық-сауықтық жүйе құлыпталады немесе осы профильдің барлық дерегі жойылады."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Экран бекітпесін ашқанда терілген қате құпия сөздердің санын бақылау және тым көп қате құпия сөздер терілсе, телефонды бекіту немесе осы пайдаланушының барлық деректерін өшіру."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Экран құлпын өзгерту"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Экран құлпын өзгерте алады."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Барлық деректерді өшіру"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Планшет дерекқорын ескертусіз, зауыттық дерекқорын қайта реттеу арқылы өшіру."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Зауыттық деректерді қалпына келтіру арқылы Android TV құрылғыңыздың деректерін ескертусіз тазартыңыз."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Зауыттық деректерді қалпына келтіру арқылы ақпараттық-сауықтық жүйе дерегі ескертусіз өшіріледі."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Зауыттық деректерге қайтару арқылы телефон деректерін ескертусіз өшіре алады."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Пайдаланушы деректерін өшіру"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Профиль дерегін өшіру"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Пайдаланушы деректерін өшіру"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Осы пайдаланушының осы планшеттегі деректерін ескертусіз өшіре алады."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Android TV құрылғысын осы пайдаланушы деректерінен ескертусіз тазартыңыз."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Осы ақпараттық-сауықтық жүйедегі профиль дерегі ескертусіз өшіріледі."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Осы пайдаланушының осы телефондағы деректерін ескертусіз өшіре алады."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Құрылғы жаһандық прокси қызметін орнату"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Саясат қосулы болғанда пайдаланылатын құрылғының ғаламдық прокси-серверін орнатыңыз. Ғаламдық прокси-серверді тек құрылғы иесі орната алады."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 6dc8abd..2889ac3 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"តាមដានការព្យាយាមដោះសោអេក្រង់"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ពិនិត្យចំនួនបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ។ ពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យទូរស័ព្ទទាំងអស់ ប្រសិនបើមានពាក្យសម្ងាត់បញ្ចូលមិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោឧបករណ៍ Android TV របស់អ្នក ឬលុបទិន្នន័យឧបករណ៍ Android TV របស់អ្នកទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ឬលុបទិន្នន័យទាំងអស់របស់ប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ពិនិត្យចំនួនបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ។ ពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យទូរស័ព្ទទាំងអស់ ប្រសិនបើមានពាក្យសម្ងាត់បញ្ចូលមិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ត្រួតពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោថេប្លេត ឬលុបទិន្នន័យអ្នកប្រើនេះទាំងអស់ ប្រសិនបើមានការវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោឧបករណ៍ Android TV របស់អ្នក ឬលុបទិន្នន័យរបស់អ្នកប្រើប្រាស់នេះទាំងអស់ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ពិនិត្យមើលចំនួនដងនៃការវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោប្រព័ន្ធព័ត៌មាននិងកម្សាន្ត ឬលុបទិន្នន័យទាំងអស់របស់កម្រងព័ត៌មាននេះ ប្រសិនបើវាយបញ្ចូលពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក។"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ត្រួតពិនិត្យចំនួននៃការវាយបញ្ចូលពាក្យសម្ងាត់ដែលមិនត្រឹមត្រូវ នៅពេលដោះសោអេក្រង់ និងចាក់សោទូរស័ព្ទ ឬលុបទិន្នន័យអ្នកប្រើនេះទាំងអស់ ប្រសិនបើមានការវាយបញ្ចូលពាក្យសម្ងាត់ខុសច្រើនដងពេក។"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ប្តូរការចាក់សោអេក្រង់"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ប្តូរការចាក់សោអេក្រង់។"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"លុបទិន្នន័យទាំងអស់"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"លុបទិន្នន័យកុំព្យូទ័របន្ទះដោយមិនព្រមានដោយអនុវត្តការកំណត់ទិន្នន័យដូចចេញពីរោងចក្រ។"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"លុបទិន្នន័យឧបករណ៍ Android TV របស់អ្នកដោយមិនមានការព្រមាន ដោយធ្វើការកំណត់ទិន្នន័យដូចចេញពីរោងចក្រ។"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"លុបទិន្នន័យរបស់ប្រព័ន្ធព័ត៌មាន និងកម្សាន្តដោយមិនមានការព្រមាន ដោយធ្វើការកំណត់ទិន្នន័យដូចចេញពីរោងចក្រ។"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"លុបទិន្នន័យទូរសព្ទដោយមិនមានការព្រមានជាមុន ដោយអនុវត្តការកំណត់ទិន្នន័យដូចចេញពីរោងចក្រ ។"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"លុបទិន្នន័យរបស់អ្នកប្រើ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"លុបទិន្នន័យកម្រងព័ត៌មាន"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"លុបទិន្នន័យរបស់អ្នកប្រើ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"លុបទិន្នន័យរបស់អ្នកប្រើនេះនៅលើថេប្លេតនេះដោយគ្មានការព្រមាន។"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"លុបទិន្នន័យរបស់អ្នកប្រើប្រាស់នេះនៅលើឧបករណ៍ Android TV នេះដោយគ្មានការព្រមាន។"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"លុបទិន្នន័យរបស់កម្រងព័ត៌មាននេះនៅលើប្រព័ន្ធព័ត៌មាន និងកម្សាន្តនេះដោយមិនមានការព្រមាន។"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"លុបទិន្នន័យរបស់អ្នកប្រើនេះនៅលើទូរស័ព្ទនេះដោយគ្មានការព្រមាន។"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"កំណត់ប្រូកស៊ីសកលរបស់ឧបករណ៍"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"កំណត់ប្រូកស៊ីសកលឧបករណ៍ដើម្បីប្រើប្រាស់ ខណៈពេលដែលគោលការណ៍បើកដំណើរការ។ មានតែឧបករណ៍ម្ចាស់ប៉ុណ្ណោះអាចកំណត់ប្រូកស៊ីសកលនេះបាន។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 180d7fc..cc9771a 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ಪರದೆಯ ಅನ್ಲಾಕ್ ಪ್ರಯತ್ನಗಳನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ, ಮತ್ತು ಟ್ಯಾಬ್ಲೆಟ್ ಅನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದ್ದರೆ ಟ್ಯಾಬ್ಲೆಟ್ನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ Android TV ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡುತ್ತದೆ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದರೆ ನಿಮ್ಮ ಎಲ್ಲಾ Android TV ಸಾಧನದ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕುತ್ತದೆ."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ ಮತ್ತು ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂ ಅನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದ್ದರೆ ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಿದಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ, ಮತ್ತು ಫೋನ್ ಅನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದ್ದರೆ ಫೋನ್ನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ಟೈಪ್ ಮಾಡಲಾದ ತಪ್ಪಾಗಿರುವ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಲಾಗಿದ್ದರೆ ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಲಾದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ ಮತ್ತು ನಿಮ್ಮ Android TV ಸಾಧನವನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಲಾಗಿದ್ದರೆ ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ ಮತ್ತು ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂ ಅನ್ನು ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಿದ್ದರೆ ಈ ಪ್ರೊಫೈಲ್ನ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ಪರದೆಯನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವಾಗ ಟೈಪ್ ಮಾಡಲಾದ ತಪ್ಪಾಗಿರುವ ಪಾಸ್ವರ್ಡ್ಗಳ ಸಂಖ್ಯೆಯನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಫೋನ್ ಲಾಕ್ ಮಾಡಿ ಅಥವಾ ಹಲವಾರು ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಟೈಪ್ ಮಾಡಲಾಗಿದ್ದರೆ ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬದಲಾಯಿಸಿ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬದಲಾಯಿಸಿ."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಿ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರೀಸೆಟ್ ಅನ್ನು ನಿರ್ವಹಿಸುವ ಮೂಲಕ ಎಚ್ಚರಿಕೆಯನ್ನು ನೀಡದೆಯೇ ಟ್ಯಾಬ್ಲೆಟ್ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರೀಸೆಟ್ ಮಾಡುವ ಮೂಲಕ ಎಚ್ಚರಿಕೆ ಇಲ್ಲದೆ ನಿಮ್ಮ Android TV ಸಾಧನದ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕುತ್ತದೆ."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರೀಸೆಟ್ ಮಾಡುವ ಮೂಲಕ ಎಚ್ಚರಿಕೆ ಇಲ್ಲದೆ ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂನ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರೀಸೆಟ್ ಅನ್ನು ನಿರ್ವಹಿಸುವ ಮೂಲಕ ಎಚ್ಚರಿಕೆಯನ್ನು ನೀಡದೆಯೇ ಫೋನ್ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕಿ."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ಬಳಕೆದಾರ ಡೇಟಾ ಅಳಿಸಿ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ಪ್ರೊಫೈಲ್ನ ಡೇಟಾವನ್ನು ಅಳಿಸಿ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ಬಳಕೆದಾರ ಡೇಟಾ ಅಳಿಸಿ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ಯಾವುದೇ ಸೂಚನೆ ಇಲ್ಲದೆ ಈ ಟ್ಯಾಬ್ಲೆಟ್ನಲ್ಲಿ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ಎಚ್ಚರಿಕೆ ಇಲ್ಲದೆ ಈ Android TV ಸಾಧನದಲ್ಲಿನ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿಹಾಕುತ್ತದೆ."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ಎಚ್ಚರಿಕೆಯಿಲ್ಲದೆ ಈ ಇನ್ಫೋಟೈನ್ಮೆಂಟ್ ಸಿಸ್ಟಂನಲ್ಲಿ ಈ ಪ್ರೊಫೈಲ್ನ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ಯಾವುದೇ ಸೂಚನೆ ಇಲ್ಲದೆ ಈ ಫೋನ್ನಲ್ಲಿ ಈ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಿ."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ಸಾಧನವನ್ನು ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಗೆ ಹೊಂದಿಸಿ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ನೀತಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ ಬಳಸಬೇಕಾದ ಸಾಧನದ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಿ. ಸಾಧನದ ಮಾಲೀಕರು ಮಾತ್ರ ಜಾಗತಿಕ ಪ್ರಾಕ್ಸಿಯನ್ನು ಹೊಂದಿಸಬಹುದಾಗಿರುತ್ತದೆ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 9c5ded9..aacc58b 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"화면 잠금 해제 시도 모니터링"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"화면 잠금해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고, 잘못된 비밀번호 입력 횟수가 너무 많은 경우 태블릿을 잠그거나 태블릿에 있는 데이터를 모두 지웁니다."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 Android TV 기기를 잠그거나 Android TV 기기의 데이터를 모두 삭제합니다."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"화면 잠금 해제 시 잘못된 비밀번호를 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 인포테인먼트 시스템을 잠그거나 인포테인먼트 시스템의 데이터를 모두 삭제합니다."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"화면 잠금해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고, 잘못된 비밀번호 입력 횟수가 너무 많은 경우 휴대전화를 잠그거나 휴대전화에 있는 데이터를 모두 지웁니다."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 태블릿을 잠그거나 이 사용자의 데이터를 모두 삭제합니다."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 Android TV 기기를 잠그거나 사용자 데이터를 모두 삭제합니다."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"화면 잠금 해제 시 잘못된 비밀번호를 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 인포테인먼트 시스템을 잠그거나 이 프로필의 데이터를 모두 삭제합니다."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"화면 잠금 해제 시 비밀번호를 잘못 입력한 횟수를 모니터링하고 잘못된 비밀번호 입력 횟수가 너무 많은 경우 휴대전화를 잠그거나 이 사용자의 데이터를 모두 삭제합니다."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"화면 잠금 변경"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"화면 잠금을 변경합니다."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"모든 데이터 삭제"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"초기화를 수행하여 경고 없이 태블릿 데이터를 지웁니다."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"초기화를 실행하여 경고 없이 Android TV 기기의 데이터를 삭제합니다."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"초기화를 실행하여 인포테인먼트 시스템의 데이터를 경고 없이 삭제합니다."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"초기화를 수행하여 경고 없이 휴대전화 데이터를 지웁니다."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"사용자 데이터 삭제"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"프로필 데이터 삭제"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"사용자 데이터 삭제"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"이 태블릿에서 사용자의 데이터를 경고 없이 삭제합니다."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Android TV 기기에서 사용자 데이터를 경고 없이 삭제합니다."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"인포테인먼트 시스템에서 이 프로필의 데이터를 경고 없이 삭제합니다."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"이 휴대전화에서 사용자의 데이터를 경고 없이 삭제합니다."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"기기 전체 프록시 설정"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"정책이 사용 설정되어 있는 동안 사용될 기기 전체 프록시를 설정합니다. 기기 소유자만 전체 프록시를 설정할 수 있습니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 87faa67..5399b4c 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран кулпусун ачуу аракеттерин көзөмөлдөө"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Экрандын кулпусу ачылып жатканда туура эмес терилген сырсөздөрдүн санын текшерип, эгер алардын саны өтө эле көп болсо, планшетти кулпулаңыз же планшеттеги бардык маалыматтарды тазалап салыңыз."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Экрандын кулпусун ачуу учурунда сырсөздөр канча жолу туура эмес терилгенин тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, Android TV түзмөгүңүздү кулпулап же Android TV түзмөгүңүздөгү бардык дайын-даректериңизди тазалап салуу."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Экрандын кулпусу ачылып жатканда туура эмес терилген сырсөздөрдүн саны текшерилип, эгер алардын саны өтө эле көп болсо, инфозоок тутуму кулпуланып же инфозоок тутумундагы бардык маалыматтар өчүрүлөт."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Экрандын кулпусу ачылып жатканда туура эмес терилген сырсөздөрдүн санын текшерип, эгер алардын саны өтө эле көп болсо, телефонду кулпулаңыз же телефондогу бардык маалыматтарды тазалап салыңыз."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Экрандын кулпусун ачуу учурунда туура эмес терилген сырсөздөрдү тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, планшетти кулпулап же бул колдонуучунун бардык дайындарын тазалап салуу."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Экрандын кулпусун ачуу учурунда сырсөздөр канча жолу туура эмес терилгенин тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, Android TV түзмөгүңүздү кулпулап же колдонуучунун бардык дайындарын тазалап салуу."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Экрандын кулпусун ачуу учурунда туура эмес терилген сырсөздөрдү тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, инфозоок тутуму кулпуланып же бул профилдин бардык дайындары өчүрүлөт."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Экрандын кулпусун ачуу учурунда туура эмес терилген сырсөздөрдү тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, телефонду кулпулап же бул колдонуучунун бардык дайындарын тазалап салуу."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Экран кулпусун өзгөртүү"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Экран кулпусун өзгөртөт."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Бардык маалыматты өчүрүү"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Алдын-ала эскертпестен, баштапкы абалга келтирүү функциясы менен планшеттеги бардык маалыматтарды өчүрөт."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Android TV түзмөгүңүздүн дайындарын эскертүүсүз кайра башынан жөндөө аркылуу тазалоо."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Алдын ала эскертпестен, баштапкы абалга келтирүү функциясы менен инфозоок тутумундагы бардык маалыматтар өчүрүлөт."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Алдын-ала эскертпестен, баштапкы абалга келтирүү функциясы менен телефондогу бардык маалыматтарды өчүрөт."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Колдонуучунун дайындарын тазалоо"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Профилдин маалыматын өчүрүү"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Колдонуучунун дайындарын тазалоо"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Бул колдонуучунун ушул планшеттеги дайындарын эскертүүсүз тазалоо."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Бул Android TV түзмөгүндөгү бул колдонуучу дайындарын эскертүүсүз тазалоо."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Алдын ала эскертпестен, бул инфозоок тутумундагы профилдин бардык маалыматтары өчүрүлөт."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Бул колдонуучунун ушул телефондогу дайындарын эскертүүсүз тазалоо."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Түзмөктүн глобалдык проксисин коюу"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Саясат иштетилгенде түзмөктүн глобалдык проксиси колдонулгудай кылып коюңуз. Түзмөк ээси гана глобалдык проксини коё алат."</string>
@@ -1252,7 +1257,7 @@
<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>
+ <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android OS тутуму менен иштеген түзмөктүн шайкеш келбеген версиясы үчүн орнотулган колдонмо жана туура эмес иштеши мүмкүн. Колдонмонун жаңырган версиясы жеткиликтүү болушу мүмкүн."</string>
<string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Ар дайым көрүнсүн"</string>
<string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Жаңыртууларды текшерүү"</string>
<string name="smv_application" msgid="3775183542777792638">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу (<xliff:g id="PROCESS">%2$s</xliff:g> процесси) өз алдынча иштеткен StrictMode саясатын бузду."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 2d8aef7..88de60d 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ຕິດຕາມການພະຍາຍາມປົດລັອກໜ້າຈໍ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ຕິດຕາມເບິ່ງຈຳນວນການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງ ໃນເວລາປົດລັອກໜ້າຈໍ ແລະລັອກແທັບເລັດ ຫຼືລຶບຂໍ້ມູນທັງໝົດຂອງແທັບເລັດ ຖ້າມີການພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງຫຼາຍເທື່ອເກີນໄປ."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ຕິດຕາມຈຳນວນລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງທີ່ພິມຕອນກຳລັງປົດລັອກໜ້າຈໍ ແລະ ລັອກອຸປະກອນ Android TV ຂອງທ່ານ ຫຼື ລຶບຂໍ້ມູນຂອງອຸປະກອນ Android TV ຂອງທ່ານຫາກພິມລະຫັດຜ່ານຜິດຫຼາຍເທື່ອເກີນໄປ."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ຕິດຕາມຈຳນວນການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງໃນເວລາປົດລັອກໜ້າຈໍ ແລະ ລັອກລະບົບສາລະບັນເທີງ ຫຼື ລຶບຂໍ້ມູນຂອງລະບົບສາລະບັນເທີງທັງໝົດຫາກມີການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງຫຼາຍເກີນໄປ."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ຕິດຕາມເບິ່ງຈຳນວນການພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ ໃນເວລາປົດລັອກໜ້າຈໍ ແລະລັອກໂທລະສັບ ຫຼືລຶບຂໍ້ມູນທັງໝົດຂອງໂປລະສັບ ຖ້າມີການພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງຫຼາຍເທື່ອເກີນໄປ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ຕິດຕາມຈຳນວນຂອງລະຫັດຜ່ານບໍ່ຖືກຕ້ອງທີ່ພິມໄປແລ້ວ ເມື່ອປົດລັອກໜ້າຈໍ, ແລະລັອກແທັບເລັດ ຫຼືລຶບທຸກຂໍ້ມູນຜູ້ໃຊ້ນີ້ ຖ້າໄດ້ພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງເຂົ້າໄປຫຼາຍອັນເກີນໄປ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ຕິດຕາມຈຳນວນຂອງລະຫັດຜ່ານບໍ່ຖືກຕ້ອງທີ່ພິມໄປແລ້ວຕອນກຳລັງປົດລັອກໜ້າຈໍ ແລະ ລັອກອຸປະກອນ Android TV ຂອງທ່ານ ຫຼື ລຶບທຸກຂໍ້ມູນຜູ້ໃຊ້ນີ້ຖ້າພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງເຂົ້າໄປຫຼາຍເທື່ອເກີນໄປ."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ຕິດຕາມຈຳນວນການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງໃນເວລາປົດລັອກໜ້າຈໍ ແລະ ລັອກລະບົບສາລະບັນເທີງ ຫຼື ລຶບຂໍ້ມູນຂອງໂປຣໄຟລ໌ນີ້ທັງໝົດຫາກມີການພິມລະຫັດຜ່ານທີ່ບໍ່ຖືກຕ້ອງຫຼາຍເກີນໄປ."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ຕິດຕາມຈຳນວນຂອງລະຫັດຜ່ານບໍ່ຖືກຕ້ອງທີ່ພິມໄປແລ້ວ ເມື່ອປົດລັອກໜ້າຈໍ, ແລະລັອກໂທລະສັບ ຫຼືລຶບທຸກຂໍ້ມູນຜູ້ໃຊ້ນີ້ ຖ້າໄດ້ພິມລະຫັດຜ່ານບໍ່ຖືກຕ້ອງເຂົ້າໄປຫຼາຍອັນເກີນໄປ."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ປ່ຽນລັອກໜ້າຈໍ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ປ່ຽນລັອກໜ້າຈໍ."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ລຶບຂໍ້ມູນທັງໝົດ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ລຶບຂໍ້ມູນຂອງແທັບເລັດໂດຍບໍ່ມີການເຕືອນ ໂດຍການຣີເຊັດກັບຄືນໃຫ້ເປັນແບບທີ່ມາຈາກໂຮງງານ."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ລຶບຂໍ້ມູນຂອງອຸປະກອນ Android TV ທ່ານໂດຍບໍ່ຕ້ອງແຈ້ງເຕືອນດ້ວຍການຣີເຊັດຂໍ້ມູນເປັນຄ່າເລີ່ມຕົ້ນຈາກໂຮງງານ."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ລຶບຂໍ້ມູນຂອງລະບົບສາລະບັນເທີງໂດຍບໍ່ມີຄຳເຕືອນດ້ວຍການດຳເນີນການຕັ້ງຄ່າຈາກໂຮງງານ."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ລຶບຂໍ້ມູນຂອງໂທລະສັບໂດຍບໍ່ມີການເຕືອນ ໂດຍການຣີເຊັດກັບຄືນໃຫ້ເປັນແບບທີ່ມາຈາກໂຮງງານ."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ລຶບຂໍ້ມູນຜູ້ໃຊ້"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ລຶບຂໍ້ມູນໂປຣໄຟລ໌"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ລຶບຂໍ້ມູນຜູ້ໃຊ້"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ລຶບຂໍ້ມູນຂອງຜູ້ໃຊ້ນີ້ຢູ່ໃນໂທລະທັດນີ້ໂດຍບໍ່ມີການເຕືອນ."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ລຶບຂໍ້ມູນຂອງຜູ້ໃຊ້ນີ້ຢູ່ອຸປະກອນ Android TV ນີ້ໂດຍບໍ່ຕ້ອງມີການເຕືອນ."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ລຶບຂໍ້ມູນຂອງໂປຣໄຟລ໌ນີ້ຢູ່ລະບົບສາລະບັນເທີງນີ້ໂດຍບໍ່ມີຄຳເຕືອນ."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ລຶບຂໍ້ມູນຂອງຜູ້ໃຊ້ນີ້ຢູ່ໃນໂທລະສັບນີ້ໂດຍບໍ່ມີການເຕືອນ."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ປ່ຽນ proxy ຮວມຂອງອຸປະກອນ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ຕັ້ງໃຫ້ພຣັອກຊີສ່ວນກາງຂອງອຸປະກອນ ທີ່ຈະໃຊ້ໃນຂະນະທີ່ເປີດນຳໃຊ້ນະໂຍບາຍ. ພຽງແຕ່ເຈົ້າຂອງອຸປະກອນເທົ່ານັ້ນສາມາດຕັ້ງພຣັອກຊີທົ່ວໄປໄດ້."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 66b30aa..180e102 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Stebėti bandymus atrakinti ekraną"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Stebimas neteisingai įvestų slaptažodžių skaičius atrakinant ekraną ir užrakinti planšetinį kompiuterį arba ištrinti visus jame esančius duomenis, jei įvedama per daug neteisingų slaptažodžių."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Stebėkite atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinkite „Android TV“ įrenginį arba ištrinkite visus „Android TV“ įrenginio duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Stebėti atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinti informacinę pramoginę sistemą arba ištrinti visus informacinės pramoginės sistemos duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Atrakindami ekraną stebėkite neteisingai įvestų slaptažodžių skaičių ir užrakinkite telefoną ar ištrinkite visus telefono duomenis, jei įvedama per daug neteisingų slaptažodžių."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Stebėkite atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinkite planšetinį kompiuterį arba ištrinkite visus šio naudotojo duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Stebėkite atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinkite „Android TV“ įrenginį arba ištrinkite visus šio naudotojo duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Stebėti atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinti informacinę pramoginę sistemą arba ištrinti visus šio profilio duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Stebėkite atrakinant ekraną įvestų netinkamų slaptažodžių skaičių ir užrakinkite telefoną arba ištrinkite visus šio naudotojo duomenis, jei per daug kartų įvedamas netinkamas slaptažodis."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Pakeisti ekrano užraktą"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Pakeisti ekrano užraktą."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Trinti visus duomenis"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Be įspėjimo ištrinti planšetinio kompiuterio duomenis atkuriant gamyklinius duomenis."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Neįspėjus ištrinami „Android TV“ įrenginio duomenys, atkūrus gamyklinius duomenis."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ištrinti informacinės pramoginės sistemos be įspėjimo atliekant gamyklinių duomenų atkūrimą."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Be įspėjimo ištrinti telefono duomenis atkuriant gamyklinius duomenis."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Naudotojo duomenų ištrynimas"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profilio duomenų ištrynimas"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Naudotojo duomenų ištrynimas"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ištrinkite šio naudotojo duomenis šiame planšetiniame kompiuteryje be įspėjimo."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ištrinami šio naudotojo duomenys šiame „Android TV“ įrenginyje be įspėjimo."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ištrinti šio profilio duomenis šioje informacinėje pramoginėje sistemoje be įspėjimo."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ištrinkite šio naudotojo duomenis šiame telefone be įspėjimo."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Nustatyti įrenginio bendrąjį tarpinį serverį"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Nustatykite įrenginio visuotinį tarpinį serverį, kuris bus naudojamas, kai politika įgalinta. Tik įrenginio savininkas gali nustatyti visuotinį tarpinį serverį."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index db4ae0b..8f3e989 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -740,9 +740,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Pārrauga nepareizi ievadīto paroļu skaitu, atbloķējot ekrānu, un bloķē planšetdatoru vai dzēš visus planšetdatora datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt Android TV vai dzēst visus Android TV ierīces datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Ekrāna atbloķēšanas laikā pārraudzīt nepareizi ievadīto paroļu skaitu un bloķēt informatīvi izklaidējošo sistēmu vai dzēst visus informatīvi izklaidējošās sistēmas datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Pārrauga nepareizi ievadīto paroļu skaitu, atbloķējot ekrānu, un bloķē tālruni vai dzēš visus tālruņa datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt planšetdatoru vai dzēst visus šī lietotāja datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt Android TV ierīci vai dzēst visus šī lietotāja datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt informatīvi izklaidējošo sistēmu vai dzēst visus šī profila datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Pārraudzīt nepareizi ievadīto ekrāna atbloķēšanas paroļu skaitu un bloķēt tālruni vai dzēst visus šī lietotāja datus, ja tiek ievadīts pārāk daudz nepareizu paroļu."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Mainīt ekrāna bloķēšanas iestatījumus"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Maina ekrāna bloķēšanas iestatījumu."</string>
@@ -751,10 +753,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Dzēst visus datus"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Dzēš planšetdatora datus bez brīdinājuma, veicot rūpnīcas datu atiestatīšanu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Dzēst Android TV ierīces datus bez brīdinājuma, veicot rūpnīcas datu atiestatīšanu."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Bez brīdinājuma dzēst informatīvi izklaidējošās sistēmas datus, veicot rūpnīcas datu atiestatīšanu."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Dzēš tālruņa datus bez brīdinājuma, veicot rūpnīcas datu atiestatīšanu."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Dzēst lietotāja datus"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profila datu dzēšana"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Dzēst lietotāja datus"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Bez brīdinājuma dzēst šī lietotāja datus no planšetdatora."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Bez brīdinājuma dzēst šī lietotāja datus no Android TV ierīces."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bez brīdinājuma dzēst šī profila datus no šīs informatīvi izklaidējošās sistēmas."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Bez brīdinājuma dzēst šī lietotāja datus no tālruņa."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Iestatīt ierīces globālo starpniekserveri"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Iestatīt ierīces globālo starpniekserveri, kas jāizmanto, kad politika ir iespējota. Globālo starpniekserveri var iestatīt tikai ierīces īpašnieks."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index e863205..9717112 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Следи ги обидите за отклучување на екранот"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Посматрај го бројот на неточни лозинки што се напишани за да се отклучи екранот и заклучи го таблетот или избриши ги сите податоци од него ако бидат напишани премногу неточни лозинки."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Го следи бројот на погрешно внесени лозинки при отклучување на екранот и го заклучува уредот Android TV или ги брише сите податоци од уредот Android TV доколку се внесени премногу погрешни лозинки."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го системот за информации и забава или избриши ги сите негови податоци доколку се внесени премногу погрешни лозинки."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Посматрај го бројот на неточни лозинки што се напишани за да се отклучи екранот и заклучи го телефонот или избриши ги сите податоци од него ако бидат напишани премногу неточни лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го таблетот или избриши ги сите податоци од овој корисник доколку се внесени премногу погрешни лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Го следи бројот на погрешно внесени лозинки при отклучување на екранот и го заклучува уредот Android TV или ги брише сите податоци од овој корисник доколку се внесени премногу погрешни лозинки."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го системот за информации и забава или избриши ги сите податоци од овој профил доколку се внесени премногу погрешни лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Набљудувај го бројот на погрешно внесени лозинки при отклучување на екранот и заклучи го телефонот или избриши ги сите податоци од овој корисник доколку се внесени премногу погрешни лозинки."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Промени го заклучувањето на екранот"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Промени го заклучувањето на екранот."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Избриши ги сите податоци"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Избриши ги податоците во таблетот без предупредување со ресетирање на фабрички податоци."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ги брише податоците на вашиот уред Android TV без предупредување, така што ќе изврши ресетирање на фабричките податоци."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Избриши ги податоците во системот за информации и забава без предупредување со ресетирање на фабрички податоци."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Избриши ги податоците во телефонот без предупредување со ресетирање на фабрички податоци."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Избриши ги податоците на корисникот"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Избриши ги податоците на профилот"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Избриши ги податоците на корисникот"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Избриши ги податоците на овој корисник на таблетот без предупредување."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ги брише податоците на овој корисник на уредов Android TV без предупредување."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Избриши ги податоците на профилов во системот за информации и забава без предупредување."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Избриши ги податоците на овој корисник на телефонот без предупредување."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Постави го глобалниот прокси на уредот"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Поставете го глобалниот прокси на уредот да се користи додека политиката е овозможена. Само сопственикот на уредот може да го поставува глобалниот прокси."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 7768d2f..12eb2a2 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"സ്ക്രീൻ അൺലോക്ക് ശ്രമങ്ങൾ നിരീക്ഷിക്കുക"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വ്ഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ടാബ്ലെറ്റ് ലോക്കുചെയ്യുകയോ ടാബ്ലെറ്റിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android TV ലോക്ക് ചെയ്യുകയോ Android TV-യിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക. നിരവധി തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്താൽ, ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റം ലോക്ക് ചെയ്യുകയോ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റത്തിന്റെ ഡാറ്റ മുഴുവനും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വ്ഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ഫോൺ ലോക്കുചെയ്യുകയോ ഫോണിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ പാസ്വേഡ് ടൈപ്പുചെയ്തെങ്കിൽ ടാബ്ലെറ്റ് ലോക്കുചെയ്യുകയോ ഈ എല്ലാ ഉപയോക്തൃവിവരവും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android TV ലോക്ക് ചെയ്യുകയോ ഈ ഉപയോക്തൃ ഡാറ്റയെല്ലാം മായ്ക്കുകയോ ചെയ്യുക."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിച്ച്, നിരവധി തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്താൽ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റം ലോക്ക് ചെയ്യുകയോ ഈ പ്രൊഫൈലിന്റെ ഡാറ്റ മുഴുവനും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ പാസ്വേഡ് ടൈപ്പുചെയ്തെങ്കിൽ ഫോൺ ലോക്കുചെയ്യുകയോ ഈ എല്ലാ ഉപയോക്തൃവിവരവും മായ്ക്കുകയോ ചെയ്യുക."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"സ്ക്രീൻ ലോക്ക് മാറ്റുക"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"സ്ക്രീൻ ലോക്ക് മാറ്റുക."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"എല്ലാ ഡാറ്റയും മായ്ക്കുക"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ഒരു ഫാക്ടറി ഡാറ്റ പുനഃസജ്ജീകരണം നടപ്പിലാക്കുന്നതിലൂടെ ടാബ്ലെറ്റിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ഫാക്ടറി ഡാറ്റ റീസെറ്റ് ചെയ്ത് നിങ്ങളുടെ Android TV-യിലെ ഉപകരണ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ഒരു ഫാക്ടറി ഡാറ്റാ റീസെറ്റിലൂടെ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റത്തിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ഒരു ഫാക്ടറി ഡാറ്റാ റീസെറ്റിലൂടെ ഫോണിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ഉപയോക്തൃ ഡാറ്റ മായ്ക്കുക"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"പ്രൊഫൈൽ ഡാറ്റ മായ്ക്കുക"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ഉപയോക്തൃ ഡാറ്റ മായ്ക്കുക"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"മുന്നറിയിപ്പൊന്നും നൽകാതെ ഈ ടാബ്ലെറ്റിലെ ഈ ഉപയോക്താവിന്റെ ഡാറ്റ മായ്ക്കുക."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ഈ Android TV-യിലെ ഈ ഉപയോക്തൃ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ഈ ഇൻഫോറ്റേയിൻമെന്റ് സിസ്റ്റത്തിലെ ഈ പ്രൊഫൈലിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"മുന്നറിയിപ്പൊന്നും നൽകാതെ ഈ ഫോണിലെ ഈ ഉപയോക്താവിന്റെ ഡാറ്റ മായ്ക്കുക."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ഉപകരണ ഗ്ലോബൽ പ്രോക്സി സജ്ജീകരിക്കുക"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"നയം പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുമ്പോൾ ഉപകരണ ഗ്ലോബൽ പ്രോക്സി ഉപയോഗിക്കുന്നത് സജ്ജമാക്കുക. ഉപകരണ ഉടമയ്ക്ക് മാത്രമേ ഗ്ലോബൽ പ്രോക്സി സജ്ജമാക്കാനാകൂ."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index e8fdc15..68fdb07 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Дэлгэцийн түгжээг тайлах оролдлогыг хянах"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Дэлгэц түгжигдсэн үед нууц үг буруу оруулалтын тоог хянах ба хэрэв хэт олон удаа нууц үгийг буруу оруулбал таблетыг түгжих болон таблетын бүх датаг арилгана"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Дэлгэцийн түгжээг тайлахаар буруу оруулсан нууц үгийн тоог хянаж, нууц үгийг хэт олон удаа буруу оруулсан тохиолдолд таны Android TV төхөөрөмжийг түгжиж эсвэл үүний бүх өгөгдлийг устгана."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Дэлгэцийн түгжээг тайлахад буруу бичиж оруулсан нууц үгний тоог хянаж, инфотэйнмент системийг түгжих эсвэл хэт олон удаа нууц үгийг буруу бичиж оруулсан тохиолдолд инфотэйнмент системийн бүх өгөгдлийг устгана."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Дэлгэц түгжигдсэн үед нууц үг буруу оруулалтын тоог хянах, ба хэрэв хэт олон удаа нууц үгийг буруу оруулбал утсыг түгжих болон утасны бүх датаг арилгана"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Дэлгэцийн түгжээг тайлахад оруулсан буруу нууц үгийн давтамжийг хянаж таблетыг түгжих эсвэл буруу нууц үгийг хэт олон удаа оруулсан тохиолдолд энэ хэрэглэгчийн мэдээллийг устгах."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Дэлгэцийн түгжээг тайлахаар буруу оруулсан нууц үгийн тоог хянаж, нууц үгийг хэт олон удаа буруу оруулсан тохиолдолд таны Android TV төхөөрөмжийг түгжиж эсвэл энэ хэрэглэгчийн бүх өгөгдлийг устгана."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Дэлгэцийн түгжээг тайлахад буруу бичиж оруулсан нууц үгний тоог хянаж, инфотэйнмент системийг түгжих эсвэл хэт олон удаа нууц үгийг буруу бичиж оруулсан тохиолдолд энэ профайлын бүх өгөгдлийг устгана."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Дэлгэцийн түгжээг тайлахад оруулсан буруу нууц үгийн давтамжийг хянаж гар утсыг түгжих эсвэл буруу нууц үгийг хэт олон удаа оруулсан тохиолдолд энэ хэрэглэгчийн мэдээллийг устгах."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Дэлгэцийн түгжээг өөрчлөх"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Дэлгэцийн түгжээг өөрчлөх."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Бүх датаг арилгах"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Үйлдвэрийн дата утгыг өгсөнөөр таблетын дата шууд арилгагдана."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Таны Android TV төхөөрөмжийн өгөгдлийг танд анхааруулалгүйгээр үйлдвэрээс гарсан төлөвт шилжүүлэн устгана."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Үйлдвэрийн өгөгдлийн төлөвт үйлдлийг гүйцэтгэснээр инфотэйнмент системийн өгөгдлийг сануулгагүйгээр устгана."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Сануулахгүйгээр утасны бүх мэдээллийг устгаж, үйлдвэрийн өгөгдмөл байдалд шилжүүлнэ"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Хэрэглэгчийн мэдээллийг арилгах"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Профайлын өгөгдлийг устгах"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Хэрэглэгчийн мэдээллийг арилгах"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Анхааруулга өгөхгүйгээр энэ хэрэглэгчийн энэ таблет дээрх мэдээллийг устгах."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Энэ Android TV төхөөрөмж дээрх хэрэглэгчийн өгөгдлийг хэрэглэгчид анхааруулалгүйгээр устгана."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Энэ инфотэйнмент систем дээр энэ профайлын өгөгдлийг сануулгагүйгээр устгана."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Анхааруулга өгөхгүйгээр энэ хэрэглэгчийн энэ гар утсан дээрх мэдээллийг устгах."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Төхөрөөмжийн глобал проксиг тохируулах"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Бодлогыг ашиглах боломжтой үед төхөөрөмжийн олон улсын эрхийг тохируулах. Зөвхөн төхөөрөмж эзэмшигч нь олон улсын эрхийг тохируулах боломжтой."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 07cb06d..bf9143d 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -552,7 +552,7 @@
<string name="permlab_disableKeyguard" msgid="3605253559020928505">"तुमचे स्क्रीन लॉक अक्षम करा"</string>
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"स्क्रीन लॉक क्लिष्टतेची विनंती करा"</string>
- <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"अॅपला स्क्रीन लॉक क्लिष्टता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य रेंज आणि स्क्रीन लॉकचा प्रकार सूचित करते. अॅप वापरकर्त्यांना असेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठराविक पातळीपर्यंत अपडेट करावे, परंतु वापरकर्ते त्याकडे मोकळेपणाने दुर्लक्ष करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string>
+ <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"अॅपला स्क्रीन लॉक क्लिष्टता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य रेंज आणि स्क्रीन लॉकचा प्रकार सूचित करते. अॅप वापरकर्त्यांना असेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठरावीक पातळीपर्यंत अपडेट करावे, परंतु वापरकर्ते त्याकडे मोकळेपणाने दुर्लक्ष करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string>
<string name="permlab_postNotification" msgid="4875401198597803658">"सूचना दाखवा"</string>
<string name="permdesc_postNotification" msgid="5974977162462877075">"ॲपला सूचना दाखवू देते"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"बायोमेट्रिक हार्डवेअर वापरा"</string>
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रीन अनलॉक प्रयत्नांचे परीक्षण करा"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"टाइप केलेल्या अयोग्य पासवर्डांच्या अंकांचे परीक्षण करा. स्क्रीन अनलॉक केली जाते, तेव्हा टॅबलेट लॉक करा किंवा बरेच पासवर्ड टाइप केले असल्यास टॅबलेटचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रीन अनलॉक करताना टाइप केलेल्या चुकीच्या पासवर्ड संख्येचे परीक्षण करते आणि Android TV डिव्हाइस लॉक करते किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास Android TV डिव्हाइसचा सर्व डेटा मिटवते."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"टाइप केलेल्या चुकीच्या पासवर्डच्या संख्येचे निरीक्षण करा. स्क्रीन अनलॉक करताना, इंफोटेनमेंट सिस्टीम लॉक करा किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास, इंफोटेनमेंट सिस्टीमचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"टाइप केलेल्या अयोग्य पासवर्डांच्या अंकांचे परीक्षण करा. स्क्रीन अनलॉक केली जाते, तेव्हा फोन लॉक करा किंवा बरेच पासवर्ड टाइप केले असल्यास फोनचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रीन अनलॉक करताना टाइप केलेल्या चुकीच्या पासवर्डांच्या संख्येचे परीक्षण करा आणि टॅबलेट लॉक करा किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास या वापरकर्त्याचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रीन अनलॉक करताना टाइप केलेल्या चुकीच्या पासवर्ड संख्येचे परीक्षण करते आणि Android TV डिव्हाइस लॉक करते किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास वापरकर्त्याचा सर्व डेटा मिटवते."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रीन अनलॉक करताना टाइप केलेल्या चुकीच्या पासवर्डच्या संख्येचे निरीक्षण करा आणि इंफोटेनमेंट सिस्टीम लॉक करा किंवा अनेक चुकीचे पासवर्ड टाइप केले असल्यास, या प्रोफाइलचा सर्व डेटा मिटवा."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"टाइप केलेल्या अयोग्य पासवर्डांच्या अंकांचे परीक्षण करा. स्क्रीन अनलॉक केली जाते, तेव्हा फोन लॉक करा किंवा बरेच पासवर्ड टाइप केले असल्यास या वापरकर्त्याचा सर्व डेटा मिटवा."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"स्क्रीन लॉक बदला"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"स्क्रीन लॉक बदला."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"सर्व डेटा मिटवा"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"फॅक्टरी डेटा रीसेट करून चेतावणीशिवाय टॅब्लेटचा डेटा मिटवा."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"चेतावणी न देता फॅक्टरी डेटा रीसेट करून Android TV डिव्हाइसचा डेटा मिटवा."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"फॅक्टरी डेटा रीसेट करून कोणत्याही चेतावणीशिवाय इंफोटेनमेंट सिस्टीमचा डेटा मिटवा."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"फॅक्टरी डेटा रीसेट करून चेतावणीशिवाय फोनचा डेटा मिटवा."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"वापरकर्ता डेटा मिटवा"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफाइल डेटा मिटवा"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"वापरकर्ता डेटा मिटवा"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"कोणत्याही चेतावणी शिवाय या वापरकर्त्याचा या टॅब्लेटवरील डेटा मिटवा."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"कोणत्याही चेतावणीशिवाय या Android TV डिव्हाइसवरील वापरकर्त्याचा डेटा मिटवा."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"कोणत्याही चेतावणीशिवाय या इंफोटेनमेंट सिस्टीमवरील प्रोफाइलचा डेटा मिटवा."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"कोणत्याही चेतावणी शिवाय या वापरकर्त्याचा या फोनवरील डेटा मिटवा."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"डिव्हाइस समग्र प्रॉक्सी सेट करा"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"धोरण सक्षम असताना वापरण्यासाठी डिव्हाइस समग्र प्रॉक्सी सेट करा. फक्त डिव्हाइस मालक समग्र प्रॉक्सी सेट करु शकतो."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 50ca2d9..05395c9 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau percubaan buka kunci skrin"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Memantau bilangan kata laluan yang tersilap ditaip apabila membuka skrin, dan mengunci tablet atau memadam semua data tablet jika terlalu banyak kesilapan menaip kata laluan."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Pantau bilangan kata laluan salah yang ditaip semasa membuka kunci skrin, dan kunci peranti Android TV anda atau padamkan semua data peranti Android TV jika terlalu banyak kata laluan yang salah ditaip."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Memantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan mengunci sistem maklumat hibur atau memadam semua data sistem maklumat hibur jika terlalu banyak kata laluan yang tidak betul ditaip."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Memantau bilangan kata laluan salah yang ditaip semasa membuka skrin, dan mengunci telefon atau memadam semua data telefon jika terlalu banyak kata laluan salah ditaip."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Pantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan kunci tablet atau padam semua data pengguna ini jika terlalu banyak kata laluan yang tidak betul ditaip."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Pantau bilangan kata laluan salah yang ditaip semasa membuka kunci skrin, dan kunci peranti Android TV anda atau padamkan semua data pengguna ini jika terlalu banyak kata laluan yang salah ditaip."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Memantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan mengunci sistem maklumat hibur atau memadam semua data profil ini jika terlalu banyak kata laluan yang tidak betul ditaip."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Pantau bilangan kata laluan tidak betul yang ditaip semasa membuka kunci skrin dan kunci telefon atau padam semua data pengguna ini jika terlalu banyak kata laluan yang tidak betul ditaip."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Tukar kunci skrin"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Tukar kunci skrin."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Padamkan semua data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Memadamkan data tablet tanpa amaran dengan melakukan tetapan semula data kilang."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Padamkan data peranti Android TV anda tanpa amaran dengan melaksanakan tetapan semula data kilang."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Memadam data sistem maklumat hibur tanpa amaran dengan melakukan tetapan semula data kilang."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Padamkan data telefon tanpa amaran dengan melakukan tetapan semula data kilang."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Padam data pengguna"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Padam data profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Padam data pengguna"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Padam data pengguna ini pada tablet ini tanpa amaran."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Padamkan data pengguna ini pada peranti Android TV tanpa amaran."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Memadam data profil pada sistem maklumat hibur ini tanpa amaran."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Padam data pengguna ini pada telefon ini tanpa amaran."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Tetapkan proksi global peranti"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Tetapkan proksi global peranti untuk digunakan sementara dasar didayakan. Hanya pemilik peranti boleh menetapkan proksi global."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 8d2c0d5..4f97ef9 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"မျက်နှာပြင်လော့ခ်ဖွင့်ရန် ကြိုးပမ်းမှုများကို စောင့်ကြည့်ပါ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"မျက်နှာပြင်ကို သော့ဖွင့်ရန် အတွက် စကားဝှက် မမှန်မကန် ထည့်သွင်းမှု အရေအတွက်ကို စောင့်ကြည့်လျက်၊ စကားဝှက် ရိုက်ထည့်မှု သိပ်များနေလျှင် တက်ဘလက်ကို သော့ခတ်ရန် သို့မဟုတ် တက်ဘလက် ဒေတာ အားလုံးကို ဖျက်ရန်။"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"မျက်နှာပြင်ကို လော့ခ်ဖွင့်သည့်အခါ စကားဝှက်မှားယွင်းစွာ ရိုက်သွင်းသည့်အကြိမ်ရေကို စောင့်ကြည့်ပြီး မှားယွင်းသည့်အကြိမ်ရေ အလွန်များလာပါက သင့် Android TV စက်ပစ္စည်းကို လော့ခ်ချခြင်း သို့မဟုတ် သင့် Android TV ရှိ အသုံးပြုသူဒေတာများအားလုံးကို ဖျက်ခြင်းတို့ ပြုလုပ်သွားပါမည်။"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ဖန်သားပြင်လော့ခ်ဖွင့်ရန် အတွက် စကားဝှက် မမှန်မကန် ထည့်သွင်းမှု အရေအတွက်ကို စောင့်ကြည့်လျက် စကားဝှက် မမှန်မကန် ရိုက်ထည့်မှု များနေလျှင် သတင်းနှင့်ဖျော်ဖြေရေး စနစ်ကို လော့ခ်ချသည် (သို့) ၎င်း၏ ဒေတာအားလုံးကို ဖျက်သည်။"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"မျက်နှာပြင်ကို သော့ဖွင့်ရန် အတွက် စကားဝှက် မမှန်မကန် ထည့်သွင်းမှု အရေအတွက်ကို စောင့်ကြည့်လျက်၊ စကားဝှက် ရိုက်ထည့်မှု သိပ်များနေလျှင် ဖုန်းကို သော့ခတ်ရန် သို့မဟုတ် ဖုန်း ဒေတာ အားလုံးကို ဖျက်ရန်။"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ဖန်မျက်နှာပြင်အား သော့ဖွင့်စဉ် လျှို့ဝှက်ကုဒ်အမှားများ ရိုက်သွင်းမှုအား စောင့်ကြည့်ရန်နှင့်၊ လျှို့ဝှက်ကုဒ်အမှားများ များစွာ ရိုက်သွင်းပါက တက်ဘလက်အား သော့ချခြင်း သို့မဟုတ် တက်ဘလက်၏ အချက်အလက်များအား ဖျက်ပစ်ခြင်းများ ပြုလုပ်မည်။"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"မျက်နှာပြင်ကို လော့ခ်ဖွင့်သည့်အခါ စကားဝှက်မှားယွင်းစွာ ရိုက်သွင်းသည့်အကြိမ်ရေကို စောင့်ကြည့်ပြီး မှားယွင်းသည့်အကြိမ်ရေ အလွန်များလာပါက သင့် Android TV စက်ပစ္စည်းကို လော့ခ်ချခြင်း သို့မဟုတ် ဤအသုံးပြုသူဒေတာများအားလုံးကို ဖျက်ခြင်းတို့ ပြုလုပ်သွားပါမည်။"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ဖန်သားပြင်လော့ခ်ဖွင့်ရန် အတွက် စကားဝှက် မမှန်မကန် ထည့်သွင်းမှု အရေအတွက်ကို စောင့်ကြည့်လျက် စကားဝှက် မမှန်မကန် ရိုက်ထည့်မှု များနေလျှင် သတင်းနှင့်ဖျော်ဖြေရေး စနစ်ကို လော့ခ်ချသည် (သို့) ဤပရိုဖိုင်၏ ဒေတာအားလုံးကို ဖျက်သည်။"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ဖန်မျက်နှာပြင်အား သော့ဖွင့်စဉ် လျှို့ဝှက်ကုဒ်အမှားများ ရိုက်သွင်းမှုအား စောင့်ကြည့်ရန်နှင့်၊ လျှို့ဝှက်ကုဒ်အမှားများ များစွာ ရိုက်သွင်းပါက ဖုန်းအား သော့ချခြင်း သို့မဟုတ် ဖုန်း၏ အချက်အလက်များအား ဖျက်ပစ်ခြင်းများ ပြုလုပ်မည်။"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ဖန်သားပြင်လော့ခ်ပြောင်းခြင်း"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ဖန်သားပြင်လော့ခ်ပြောင်းသည်။"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ဒေတာအားလုံးအားဖျက်ခြင်း"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"စက်ရုံထုတ် အခြေအနေအား ပြန်ပြောင်းခြင်းဖြင့် တက်ဘလက်ရှိ အချက်အလက်များအား ကြိုတင်သတိပေးမှုမရှိပဲ ဖျက်စီးရန်"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"စက်ရုံထုတ်အခြေအနေ ပြန်ယူရန် လုပ်ဆောင်ခြင်းဖြင့် သင့် Android TV စက်ပစ္စည်းပေါ်ရှိ ဒေတာများကို သတိမပေးဘဲ ဖျက်လိုက်ပါမည်။"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"စက်ရုံထုတ်အခြေအနေပြန်ယူခြင်းကို ဆောင်ရွက်ခြင်းဖြင့် သတင်းနှင့်ဖျော်ဖြေရေး စနစ်၏ ဒေတာကို သတိပေးချက် မပေးဘဲ ဖျက်သည်။"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"စက်ရုံထုတ်အခြေအနေသို့ ပြန်ပြောင်းခြင်းဖြင့် ဖုန်းရှိဒေတာများကို သတိပေးခြင်း မရှိဘဲ ဖျက်သည်။"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"အသုံးပြုသူဒေတာကို ဖျက်မည်"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ပရိုဖိုင်ဒေတာ ဖျက်ခြင်း"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"အသုံးပြုသူဒေတာကို ဖျက်မည်"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"သတိပေးခြင်းမရှိဘဲ ဤတက်ဘလက်ပေါ်ရှိ ထိုအသုံးပြုသူ၏ဒေတာအား ဖျက်မည်။"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ဤ Android TV စက်ပစ္စည်းပေါ်ရှိ အသုံးပြုသူဒေတာများကို သတိပေးခြင်းမရှိဘဲ ဖျက်ပါမည်။"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ဤသတင်းနှင့်ဖျော်ဖြေရေး စနစ်ရှိ ပရိုဖိုင်၏ ဒေတာကို သတိပေးချက် မပေးဘဲ ဖျက်သည်။"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"သတိပေးခြင်းမရှိဘဲ ဤဖုန်းပေါ်ရှိ ထိုအသုံးပြုသူ၏ဒေတာအား ဖျက်မည်။"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"တကမာ္ဘလုံးဆိုင်ရာပရော်စီကို သတ်မှတ်ခြင်း"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ပေါ်လစီအား ဖွင့်ထားချိန်တွင် တစ်ကမ္ဘာလုံးဆိုင်ရာ ပရောက်စီအား အသုံးပြုရန် ကိရိယာကို သတ်မှတ်မည်။ ကိရိယာ၏ ပိုင်ရှင်သာ ကမ္ဘာလုံးဆိုင်ရာ ပရောက်စီကို သတ်မှတ်နိုင်သည်။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index e26e41a..ccf5e97 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåk forsøk på å låse opp skjermen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Overvåk antall feil passordforsøk ved opplåsing av skjerm, og lås nettbrettet eller slett alle data fra nettbrettet ved for mange feil passordforsøk."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Følg med på hvor mange ganger feil passord skrives inn når skjermen skal låses opp, og lås Android TV-enheten eller tøm alle dataene når feil passord skrives inn for mange ganger."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Overvåk antall feil passord som er skrevet inn ved opplåsing av skjermen, og lås infotainment-systemet eller tøm alle dataene i infotainment-systemet hvis feil passord skrives inn for mange ganger."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Overvåk antall feil passordforsøk ved opplåsing av skjerm, og lås telefonen eller slett alle data fra telefonen ved for mange feil passordforsøk."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser nettbrettet eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Følg med på hvor mange ganger feil passord skrives inn når skjermen skal låses opp, og lås Android TV-enheten eller tøm alle dataene til denne brukeren hvis feil passord skrives inn for mange ganger."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Overvåk antall feil passord som er skrevet inn ved opplåsing av skjermen, og lås infotainment-systemet eller tøm alle dataene i denne profilen hvis feil passord skrives inn for mange ganger."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser telefonen eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Endring av skjermlåsen"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Kan endre skjermlåsen."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Sletting av alle data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Tilbakestill nettbrettets data uten advarsel ved å tilbakestille til fabrikkstandard."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Tøm dataene på Android TV-enheten din uten advarsel ved å tilbakestille til fabrikkstandard."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Tøm dataene i infotainment-systemet uten varsel ved å tilbakestille til fabrikkstandard."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Telefonens data kan slettes uten advarsel ved å tilbakestille til fabrikkstandard."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Slett brukerdataene"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Tøm profildata"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Slett brukerdataene"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Sletter denne brukerens data på dette nettbrettet uten advarsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Tøm dataene til denne brukeren på denne Android TV-enheten uten advarsel."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Tøm dataene i denne profilen på dette infotainment-systemet uten varsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Sletter denne brukerens data på denne telefonen uten advarsel."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Angi enhetens globale proxy-tjener"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Angir den globale proxy-tjeneren på enheten som skal brukes når regelen er aktivert. Bare eieren av enheten kan angi den globale proxy-tjeneren."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index b741507..99e3375 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"मनिटरको स्क्रिन अनलक गर्ने प्रयासहरू"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने ट्याब्लेट लक गर्नुहोस् वा ट्याब्लेटका सबै डेटा मेट्नुहोस्।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा डिभाइसमा भएको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गरियोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गरियोस् वा यस इन्फोटेनमेन्ट प्रणालीका सबै डेटा मेटाइयोस्।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रिनअनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने फोन लक गर्नुहोस् वा फोनका सबै डेटा मेट्नुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा ट्याब्लेट लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा यो प्रयोगकर्ताको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रिन अनलक गर्दा कति पटक गलत पासवर्ड टाइप गरिन्छ भन्ने कुरा निगरानी गरियोस् र अत्यन्तै धेरै पटक गलत पासवर्ड टाइप गरिएका खण्डमा यो इन्फोटेनमेन्ट प्रणाली लक गरियोस् वा यस प्रोफाइलका सबै डेटा मेटाइयोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा फोन लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"स्क्रिन लक परिवर्तन गर्ने"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"स्क्रिन लक परिवर्तन गर्नुहोस्।"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"सबै डेटा मेट्ने"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नआउँदै ट्याबल्टको डेटा मेट्नुहोस्।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ्याक्ट्री डेटा रिसेट गरेर चेतावनी नदिइकन आफ्नो Android टिभी डिभाइसको डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"यो इन्फोटेनमेन्ट प्रणालीको डेटा कुनै चेतावनीविनै फ्याक्ट्री डेटा रिसेट गरेर मेटाइयोस्।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नदिइकन फोनको डेटा मेट्न।"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"प्रयोगकर्ता डेटा मेट्नुहोस्"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफाइल डेटा मेटाइयोस्"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"प्रयोगकर्ता डेटा मेट्नुहोस्"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"चेतावनी बिना यो ट्याब्लेटमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"यो Android टिभी डिभाइसमा भएको यस प्रयोगकर्ताको डेटा चेतावनी नदिइकन मेटाउनुहोस्।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"यो इन्फोटेनमेन्ट प्रणालीमा भएको यस प्रोफाइलको डेटा कुनै चेतावनीविनै मेटाइयोस्।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"चेतावनी बिना यो फोनमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"उपकरण विश्वव्यापी प्रोक्सी मिलाउनुहोस्"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"नीति सक्षम हुँदा प्रयोग गरिनको लागि यन्त्र ग्लोवल प्रोक्सी सेट गर्नुहोस्। केवल यन्त्र मालिकले ग्लोवल प्रोक्सी सेट गर्न सक्नुहुन्छ।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9bd6a28..a1b45bf 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pogingen voor schermontgrendeling bijhouden"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de tablet vergrendelen of alle gegevens op de tablet wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en het Android TV-apparaat vergrendelen of alle gegevens van het Android TV-apparaat wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Bijhouden hoe vaak onjuiste wachtwoorden worden getypt als het scherm wordt ontgrendeld, en het infotainmentsysteem vergrendelen of alle gegevens op het infotainmentsysteem wissen als te veel onjuiste wachtwoorden worden getypt."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens op de telefoon wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de tablet vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en het Android TV-apparaat vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Bijhouden hoe vaak onjuiste wachtwoorden worden getypt als het scherm wordt ontgrendeld, en het infotainmentsysteem vergrendelen of alle gegevens van dit profiel wissen als te veel onjuiste wachtwoorden worden getypt."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"De schermvergrendeling wijzigen"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Wijzig de schermvergrendeling."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Alle gegevens wissen"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"De gegevens van de tablet zonder waarschuwing wissen door de fabrieksinstellingen te herstellen."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"De gegevens van het Android TV-apparaat zonder waarschuwing wissen door de fabrieksinstellingen terug te zetten."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"De gegevens van het infotainmentsysteem zonder waarschuwing wissen door de fabrieksinstellingen terug te zetten."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Wis de gegevens van de telefoon zonder waarschuwing door de fabrieksinstellingen te herstellen."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Gebruikersgegevens wissen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profielgegevens wissen"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Gebruikersgegevens wissen"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"De gegevens van deze gebruiker op deze tablet zonder waarschuwing wissen."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"De gegevens van deze gebruiker op dit Android TV-apparaat zonder waarschuwing wissen."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"De gegevens van dit profiel op het infotainmentsysteem zonder waarschuwing wissen."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"De gegevens van deze gebruiker op deze telefoon zonder waarschuwing wissen."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Algemene proxy voor het apparaat instellen"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"De algemene proxy voor het apparaat instellen die moet worden gebruikt terwijl het beleid is geactiveerd. Alleen de eigenaar van het apparaat kan de algemene proxy instellen."</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 29aea5e..afe0441 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ସ୍କ୍ରୀନ୍-ଅନଲକ୍ କରିବା ଉଦ୍ୟମ ନୀରିକ୍ଷଣ କରନ୍ତୁ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଟାବଲେଟ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ସ୍କ୍ରିନ ଅନଲକ କରିବା ସମୟରେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ମନିଟର କରନ୍ତୁ ଏବଂ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମକୁ ଲକ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମର ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଫୋନ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ସ୍କ୍ରିନ ଅନଲକ କରିବା ସମୟରେ ଟାଇପ କରାଯାଇଥିବା ଭୁଲ ପାସୱାର୍ଡର ସଂଖ୍ୟାକୁ ମନିଟର କରନ୍ତୁ ଏବଂ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମକୁ ଲକ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକଗୁଡ଼ିଏ ଭୁଲ ପାସୱାର୍ଡ ଟାଇପ କରାଯାଇଥାଏ ତେବେ ଏହି ପ୍ରୋଫାଇଲର ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ସ୍କ୍ରିନ୍ ଲକ୍ ବଦଳାଏ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ସ୍କ୍ରିନ୍ ଲକ୍ ବଦଳାଏ।"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ସମସ୍ତ ଡାଟା ଖାଲି କରିବା"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ସେଟିଙ୍ଗ କରାଇ ଟାବ୍ଲେଟ୍ର ଡାଟା ଲିଭାଇଥାଏ।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ବିନା ଚେତାବନୀରେ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ର ଡାଟା ଲିଭାନ୍ତୁ।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ କରି ବିନା ଚେତାବନୀରେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମର ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ଫୋନ୍ର ଡାଟା ଲିଭାଇଥାଏ।"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ୟୁଜର୍ ଡାଟା ଲିଭାନ୍ତୁ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ପ୍ରୋଫାଇଲ ଡାଟା ଖାଲି କରନ୍ତୁ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ୟୁଜର୍ ଡାଟା ଲିଭାନ୍ତୁ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ବିନା ଚେତାବନୀରେ ଏହି ଟାବଲେଟରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ବିନା ଚେତାବନୀରେ ଏହି Android TV ଡିଭାଇସ୍ରେ ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ବିନା ଚେତାବନୀରେ ଇନଫୋଟେନମେଣ୍ଟ ସିଷ୍ଟମରେ ଥିବା ଏହି ପ୍ରୋଫାଇଲର ଡାଟା ଖାଲି କରନ୍ତୁ।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ବିନା ଚେତାବନୀରେ ଏହି ଫୋନରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ଗ୍ଲୋବଲ୍ ପ୍ରକ୍ସୀ ଡିଭାଇସ୍କୁ ସେଟ୍ କରନ୍ତୁ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ପଲିସୀ ସକ୍ଷମ କରାଯାଇଥିବାବେଳେ ବ୍ୟବହାର କରିବା ପାଇଁ ଗ୍ଲୋବାଲ୍ ପ୍ରକ୍ସୀ ସେଟ୍ କରନ୍ତୁ। କେବଳ ଡିଭାଇସ୍ ମାଲିକ ଗ୍ଲୋବାଲ୍ ପ୍ରକ୍ସୀ ସେଟ୍ କରିପାରିବେ।"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index a619f18..1877ebe 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ਸਕ੍ਰੀਨ ਅਣਲਾਕ ਕਰਨ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ \'ਤੇ ਨਿਗਰਾਨੀ ਰੱਖੋ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"ਸਕ੍ਰੀਨ ਲਾਕ ਬਦਲੋ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"ਸਕ੍ਰੀਨ ਲਾਕ ਬਦਲੋ।"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ਇੱਕ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਟੈਬਲੈੱਟ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਤੁਹਾਡੇ Android TV ਡੀਵਾਈਸ ਦਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ।"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ਇੱਕ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਫ਼ੋਨ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ਉਪਭੋਗਤਾ ਡਾਟਾ ਮਿਟਾਓ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ਪ੍ਰੋਫਾਈਲ ਡਾਟਾ ਮਿਟਾਓ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ਉਪਭੋਗਤਾ ਡਾਟਾ ਮਿਟਾਓ"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ ਟੈਬਲੈੱਟ ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ Android TV ਡੀਵਾਈਸ \'ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ਇਸ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ \'ਤੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ ਫ਼ੋਨ ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ਡੀਵਾਈਸ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰੋ"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ਜਦੋਂ ਨੀਤੀ ਚਾਲੂ ਹੋਵੇ ਤਾਂ ਵਰਤੇ ਜਾਣ ਲਈ ਡੀਵਾਈਸ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰੋ। ਕੇਵਲ ਡੀਵਾਈਸ ਮਾਲਕ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰ ਸਕਦਾ ਹੈ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index af2b120..6f2198e 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorowanie prób odblokowania ekranu"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Przy odblokowywaniu ekranu monitoruj, ile razy wpisano nieprawidłowe hasło i blokuj tablet lub usuń z niego wszystkie dane, jeśli nieprawidłowe hasło podano zbyt wiele razy."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorowanie liczby nieudanych prób odblokowania ekranu za pomocą hasła oraz blokowanie urządzenia z Androidem TV lub kasowanie z niego wszystkich danych w razie wpisania błędnego hasła zbyt wiele razy."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorowanie przypadków nieprawidłowego wpisania hasła podczas odblokowywania ekranu i blokowanie systemu multimedialno-rozrywkowego lub usuwanie z niego wszystkich danych przy zbyt dużej liczbie błędnych prób."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Przy odblokowywaniu ekranu monitoruje, ile razy wpisano nieprawidłowe hasło, i blokuje telefon lub usuwa z niego wszystkie dane, jeśli nieprawidłowe hasło podano zbyt wiele razy"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorowanie, ile razy wpisano błędne hasło podczas odblokowywania ekranu, oraz blokowanie tabletu albo kasowanie wszystkich danych tego użytkownika, gdy zbyt wiele razy wpisano błędne hasło."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorowanie, ile razy wpisano błędne hasło podczas odblokowywania ekranu, oraz blokowanie urządzenia z Androidem TV albo kasowanie wszystkich danych tego użytkownika, gdy błędne hasło zostało wpisane zbyt wiele razy."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorowanie przypadków nieprawidłowego wpisania hasła podczas odblokowywania ekranu i blokowanie systemu multimedialno-rozrywkowego lub usuwanie wszystkich danych z profilu przy zbyt dużej liczbie błędnych prób."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorowanie, ile razy wpisano błędne hasło podczas odblokowywania ekranu, oraz blokowanie telefonu albo kasowanie wszystkich danych tego użytkownika, gdy zbyt wiele razy wpisano błędne hasło."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Zmiana blokady ekranu"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Pozwala zmienić blokadę ekranu."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Usuwanie wszystkich danych"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Wymazywanie danych z tabletu bez ostrzeżenia przez przywrócenie danych fabrycznych"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Wymazywanie danych z urządzenia z Androidem TV bez ostrzeżenia przez przywrócenie danych fabrycznych."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Wykasowanie danych z systemu multimedialno-rozrywkowego bez ostrzeżenia przez przywrócenie danych fabrycznych."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Wymazywanie danych z telefonu bez ostrzeżenia przez przywrócenie danych fabrycznych."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Kasuj dane użytkownika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Wykasuj dane z profilu"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Kasuj dane użytkownika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Kasowanie danych tego użytkownika na tym tablecie bez ostrzeżenia."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Kasowanie danych tego użytkownika na tym urządzeniu z Androidem TV bez ostrzeżenia."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Wykasowanie danych z profilu w tym systemie multimedialno-rozrywkowym bez ostrzeżenia."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Kasowanie danych tego użytkownika na tym telefonie bez ostrzeżenia."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Ustaw globalny serwer proxy urządzenia"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Ustawianie globalnego serwera proxy urządzenia do użycia przy włączonych zasadach. Tylko właściciel urządzenia może ustawić globalny serwer proxy."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 1f9c0775..8e7cc3d 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorar tentativas de desbloqueio de tela"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o dispositivo Android TV ou limpa todos os dados nele se muitas senhas incorretas forem digitadas."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o sistema de infoentretenimento ou apaga todos os dados desse sistema quando isso acontece muitas vezes."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o tablet ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o dispositivo Android TV ou apaga todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o sistema de infoentretenimento ou apaga todos os dados do perfil quando isso acontece muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o smartphone ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Alterar o bloqueio de tela"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Altera o bloqueio de tela."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Apague os dados do tablet sem aviso redefinindo a configuração original."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Redefine o dispositivo Android TV para a configuração original e apaga os dados sem aviso."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Apaga os dados sem aviso, redefinindo o sistema de infoentretenimento para a configuração original."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Apaga os dados sem aviso redefinindo o smartphone para a configuração original."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Limpar dados do usuário"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Apagar dados do perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Limpar dados do usuário"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Limpa os dados do usuário neste tablet sem aviso prévio."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Limpa os dados do usuário neste dispositivo Android TV sem aviso prévio."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Apaga os dados do perfil do sistema de infoentretenimento sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Limpa os dados do usuário neste smartphone sem aviso prévio."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir o proxy global do dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Configura o proxy global do dispositivo para ser usado enquanto a política está ativada. Somente o proprietário do dispositivo pode definir o proxy global."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 98ac85e..bf8ff8e 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorizar tentativas de desbloqueio do ecrã"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitorizar o número de palavras-passe incorretas escritas ao desbloquear o ecrã e bloquear o tablet ou apagar todos os dados do tablet, se forem escritas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o seu dispositivo Android TV ou apagar todos os dados do mesmo se forem introduzidas demasiadas palavras-passe incorretas."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorize o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloqueie o sistema de infoentretenimento ou apague todos os dados do sistema de infoentretenimento, se forem escritas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o telemóvel ou apagar todos os dados do telemóvel caso tenham sido introduzidas demasiadas palavras-passe."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o tablet ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o dispositivo Android TV ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorize o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloqueie o sistema de infoentretenimento ou apague todos os dados deste utilizador, se forem introduzidas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o telemóvel ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Alterar o bloqueio de ecrã"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Altera o bloqueio de ecrã."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Apagar os dados do tablet sem avisar através de uma reposição de dados de fábrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Apagar os dados do seu dispositivo Android TV sem avisar ao efetuar uma reposição de dados de fábrica."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Apague os dados do sistema de infoentretenimento sem aviso ao executar uma reposição de dados de fábrica."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Apaga os dados do telemóvel sem avisar ao efetuar uma reposição de dados de fábrica."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Apagar os dados do utilizador"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Apague os dados do perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Apagar os dados do utilizador"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Apagar os dados deste utilizador neste tablet sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Apagar os dados deste utilizador neste dispositivo Android TV sem aviso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Apague os dados deste perfil neste sistema de infoentretenimento sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Apagar os dados deste utilizador neste telemóvel sem aviso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir o proxy global do aparelho"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Definir o proxy global do dispositivo a utilizar enquanto a política está ativada. Apenas o proprietário do dispositivo pode definir o proxy global."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 1f9c0775..8e7cc3d 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorar tentativas de desbloqueio de tela"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o dispositivo Android TV ou limpa todos os dados nele se muitas senhas incorretas forem digitadas."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o sistema de infoentretenimento ou apaga todos os dados desse sistema quando isso acontece muitas vezes."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o telefone ou apaga todos os dados do telefone se a senha for digitada incorretamente muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o tablet ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o dispositivo Android TV ou apaga todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitora quantas vezes a senha foi digitada incorretamente ao desbloquear a tela e bloqueia o sistema de infoentretenimento ou apaga todos os dados do perfil quando isso acontece muitas vezes."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitora o número de senhas incorretas digitadas ao desbloquear a tela e bloqueia o smartphone ou limpa todos os dados do usuário se muitas senhas incorretas forem digitadas."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Alterar o bloqueio de tela"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Altera o bloqueio de tela."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Apague os dados do tablet sem aviso redefinindo a configuração original."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Redefine o dispositivo Android TV para a configuração original e apaga os dados sem aviso."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Apaga os dados sem aviso, redefinindo o sistema de infoentretenimento para a configuração original."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Apaga os dados sem aviso redefinindo o smartphone para a configuração original."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Limpar dados do usuário"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Apagar dados do perfil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Limpar dados do usuário"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Limpa os dados do usuário neste tablet sem aviso prévio."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Limpa os dados do usuário neste dispositivo Android TV sem aviso prévio."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Apaga os dados do perfil do sistema de infoentretenimento sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Limpa os dados do usuário neste smartphone sem aviso prévio."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir o proxy global do dispositivo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Configura o proxy global do dispositivo para ser usado enquanto a política está ativada. Somente o proprietário do dispositivo pode definir o proxy global."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 5bbad82..8f05f9f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -740,9 +740,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Să monitorizeze încercările de deblocare a ecranului"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți datele acesteia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați dispozitivul Android TV sau ștergeți toate datele de pe acesta dacă se introduc prea multe parole incorecte."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați sistemul de infotainment sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați dispozitivul Android TV sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați sistemul de infotainment sau ștergeți toate datele acestui profil dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Să schimbe blocarea ecranului"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Modificați blocarea ecranului."</string>
@@ -751,10 +753,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Să șteargă toate datele"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ștergeți datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ștergeți datele de pe dispozitivul Android TV fără avertisment, efectuând o revenire la setările din fabrică."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ștergeți datele din sistemul de infotainment fără avertisment, prin revenirea la setările din fabrică."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ștergeți datele din telefon fără avertisment, efectuând resetarea configurării din fabrică."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Ștergeți datele utilizatorului"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Ștergeți datele de profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Ștergeți datele utilizatorului"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ștergeți datele acestui utilizator de pe această tabletă fără avertisment."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ștergeți datele acestui utilizator de pe acest dispozitiv Android TV fără avertisment"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ștergeți datele profilului din acest sistem de infotainment fără avertisment."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ștergeți datele acestui utilizator de pe acest telefon fără avertisment."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Setați serverul proxy global pentru dispozitiv"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Setați serverul proxy global pentru dispozitiv, care să fie utilizat cât timp politica este activă. Numai proprietarul dispozitivului poate seta serverul proxy global."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index b8f190f..927ec93 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Отслеживание попыток разблокировать экран"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Отслеживает попытки ввода пароля при разблокировке экрана и блокирует планшетный ПК или удаляет с него все данные, если было сделано слишком много таких попыток."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Блокировать устройство Android TV или удалять с него все ваши данные при слишком большом количестве неудачных попыток ввести пароль для разблокировки экрана."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Блокировать информационно-развлекательную систему или удалять из нее все данные, если совершено слишком много неудачных попыток ввести пароль для разблокировки экрана."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Отслеживает попытки ввода пароля при разблокировке экрана и блокирует телефон или удаляет с него все данные, если было сделано слишком много таких попыток."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Отслеживать неверно введенные пароли при разблокировке экрана и блокировать планшет или удалять с него все данные, если сделано слишком много неудачных попыток."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Блокировать устройство Android TV или удалять с него все данные этого пользователя при слишком большом количестве неудачных попыток ввести пароль для разблокировки экрана."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Блокировать информационно-развлекательную систему или удалять все данные профиля, если совершено слишком много неудачных попыток ввести пароль для разблокировки экрана."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Отслеживать неверно введенные пароли при разблокировке экрана и блокировать телефон или удалять с него все данные, если сделано слишком много неудачных попыток."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Изменение способа блокировки экрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Изменять способ блокировки экрана."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Удаление всех данных"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Удалять все данные на планшетном ПК без предупреждения путем сброса настроек."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Удалять данные с устройства Android TV без предупреждения, выполняя восстановление заводских настроек."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Сбрасывать настройки без предупреждения, таким образом удаляя все данные из информационно-развлекательной системы."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Удалять все данные на телефоне без предупреждения путем сброса настроек."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Удалить пользовательские данные"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Удаление данных профиля"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Удалить пользовательские данные"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Удалить данные этого пользователя с планшета без предупреждения."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Удалять данные этого пользователя с устройства Android TV без предупреждения."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Удалять данные профиля из этой информационно-развлекательной системы без предупреждения."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Удалить данные этого пользователя с телефона без предупреждения."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Глобальный прокси-сервер"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Настроить глобальный прокси-сервер устройства, который будет использоваться при активной политике. Это может сделать только владелец устройства."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 6b1b614..c63708f 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"තිරය අගුළු ඇරීමේ උත්සාහයන් නිරීක්ෂණය කරන්න"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"තිරය අගුළු හැරීමේදී වැරදියට ටයිප් කළ මුරපද ගණන නිරීක්ෂණය කරන්න සහ ටැබ්ලටය අගුළු දමන්න හෝ වැරදි මුරපද බොහෝ ගණනක් ටයිප් කර ඇති නම් ටැබ්ලටයේ සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් ඔබේ Android TV උපාංගය අගුලු දමන්න නැතහොත් මෙම Android TV උපාංගයෙහි සියලු දත්ත මකන්න."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් තොරතුරු විනෝදාස්වාද පද්ධතිය අගුලු දමන්න නැතහොත් මෙම තොරතුරු විනෝදාස්වාද පද්ධතියෙහි සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"තිරය අගුළු හැරීමේදී වැරදියට ටයිප් කළ මුරපද ගණන නිරීක්ෂණය කරන්න සහ දුරකථනය අගුළු දමන්න හෝ වැරදි මුරපද බොහෝ ගණනක් ටයිප් කර ඇති නම් දුරකථනයේ සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් ටැබ්ලටය අගුලු දමන්න නැතහොත් මෙම පරිශීලකයාගේ සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් ඔබේ Android TV අගුලු දමන්න නැතහොත් මෙම පරිශීලකයාගේ සියලු දත්ත මකන්න."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් තොරතුරු විනෝදාස්වාද පද්ධතිය අගුලු දමන්න නැතහොත් මෙම පැතිකඩෙහි සියලු දත්ත මකන්න."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"තිරය අගුලු හරින විට වැරදියට මුරපදය ටයිප් කළ වාර ගණන නිරීක්ෂණය කර, ඉතා වැඩි වාර ගණනක් වැරදි මුරපද ටයිප් කළේ නම් දුරකථනය අගුලු දමන්න නැතහොත් මෙම පරිශීලකයාගේ සියලු දත්ත මකන්න."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"තිර අගුල වෙනස් කරන්න"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"තිර අගුල වෙනස් කරන්න."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"සියලු දත්ත මකන්න"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"කර්මාන්ත ශාලා දත්ත යළි පිහිටුවීමෙන් පසුව අනතුරු ඇඟවිමකින් තොරවම ටැබ්ලට් දත්ත මකා දමයි."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"කර්මාන්ත ශාලා දත්ත යළි සැකසීමක් සිදු කිරීම මගින්, අනතුරු ඇඟවිමකින් තොරව ඔබේ Android TV දත්ත මකයි."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"අනතුරු ඇඟවීමෙන් තොරව කර්මාන්තශාලා දත්ත යළි සැකසීමක් සිදු කිරීම මගින් තොරතුරු විනෝදාස්වාද පද්ධතියේ දත්ත මකන්න."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"කර්මාන්ත ශාලා දත්ත යළි පිහිටුවීමෙන් පසුව අනතුරු ඇඟවිමකින් තොරවම දුරකථන දත්ත මකා දමයි."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"පරිශීලක දත්ත මකන්න"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"පැතිකඩ දත්ත මකන්න"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"පරිශීලක දත්ත මකන්න"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"අනතුරු ඇඟවීමකින් තොරව මෙම ටැබ්ලටයෙහි මෙම පරිශීලකයාගේ දත්ත මැකීම."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"අනතුරු ඇඟවීමකින් තොරව මෙම Android TV උපාංගයෙහි මෙම පරිශීලකයාගේ දත්ත මැකීම."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"අනතුරු ඇඟවීමෙන් තොරව මෙම තොරතුරු විනෝදාස්වාද පද්ධතියේ මෙම පැතිකඩෙහි දත්ත මකන්න."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"අනතුරු ඇඟවීමකින් තොරව මෙම දුරකථනයෙහි මෙම පරිශීලකයාගේ දත්ත මැකීම."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"උපාංග ගෝලීය නියුතුව සකස් කිරීම"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ප්රතිපත්තිය සක්රිය අතරතුර ගෝලීය ප්රොක්සි භාවිත කිරීමට උපාංගය සකසන්න. උපාංග හිමිකරුට පමණක් ගෝලීය ප්රොක්සි සැකසිය හැකිය."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 081f901..6645176 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovanie pokusov o odomknutie obrazovky"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Sledovať počet nesprávnych hesiel zadaných pri odomykaní obrazovky a zamknúť tablet alebo vymazať všetky údaje tabletu v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Sledovanie počtu nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknutie zariadenia Android TV alebo vymazanie všetkých jeho údajov."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a uzamknite palubný systém alebo vymažte všetky údaje v palubnom systéme v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Sledovať počet nesprávnych hesiel zadaných pri odomykaní obrazovky a zamknúť telefón alebo vymazať všetky údaje v telefóne v prípade príliš veľkého počtu neplatných pokusov o zadanie hesla."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknite tablet alebo vymažte všetky údaje tohto používateľa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Sledovanie počtu nesprávnych hesiel zadaných pri odomykaní obrazovky a ak je ich zadaných príliš mnoho, uzamknutie zariadenia Android TV alebo vymazanie všetkých údajov tohto používateľa."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknite palubný systém alebo vymažte všetky údaje tohto profilu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Sledujte počet nesprávnych hesiel zadaných pri odomykaní obrazovky a v prípade, že ich je zadaných príliš mnoho, uzamknite telefón alebo vymažte všetky údaje tohto používateľa."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Zmeniť zámku obrazovky"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Zmeniť zámku obrazovky."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Vymazať všetky dáta"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Bez predchádzajúceho upozornenia vymazať všetky dáta obnovením výrobných nastavení tabletu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Vymazanie údajov v zariadení Android TV bez upozornenia obnovením výrobných nastavení."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Vymažte údaje palubného systému bez upozornenia obnovením výrobných nastavení."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Bez predchádzajúceho upozornenia vymazať všetky dáta obnovením výrobných nastavení telefónu."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Vymazať údaje používateľa"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Vymazanie údajov profilu"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Vymazať údaje používateľa"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Vymažte bez upozornenia údaje tohto používateľa na tomto tablete."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Vymazanie údajov tohto používateľa v tomto zariadení Android TV bez upozornenia."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Vymažte údaje tohto profilu v tomto palubnom systéme bez upozornenia."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Vymažte bez upozornenia údaje tohto používateľa na tomto telefóne."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Nastaviť globálny server proxy zariadenia"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Vyberte globálny proxy server, ktorý sa bude používať po aktivácii pravidiel. Nastaviť ho môže iba vlastník zariadenia."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index c14dd45..935b1e8 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadzor nad poskusi odklepanja zaslona"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Nadzoruje število nepravilno vnesenih gesel pri odklepanju zaslona in zaklene tablični računalnik ali izbriše vse podatke v njem, če je vnesenih preveč nepravilnih gesel."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Nadzira število vnesenih nepravilnih gesel pri odklepanju zaslona in zaklene napravo Android TV ali izbriše vse podatke v napravi Android TV, če je vnesenih preveč nepravilnih gesel."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Spremljanje števila nepravilno vnesenih gesel pri odklepanju zaslona in zaklenitev informativno-razvedrilnega sistema ali izbris vseh podatkov v njem v primeru preveč vnosov nepravilnih gesel"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Spremljajte število vnesenih napačnih gesel, s katerimi želite odkleniti zaslon. Če je teh vnosov preveč, zaklenite telefon ali izbrišite vse podatke v njem."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Nadzira število vnesenih nepravilnih gesel pri odklepanju zaslona in zaklene tablični računalnik ali izbriše vse podatke lastnika, če je vnesenih preveč nepravilnih gesel."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Nadzira število vnesenih nepravilnih gesel pri odklepanju zaslona in zaklene napravo Android TV ali izbriše vse podatke tega uporabnika, če je vnesenih preveč nepravilnih gesel."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Spremljanje števila nepravilno vnesenih gesel pri odklepanju zaslona in zaklenitev informativno-razvedrilnega sistema ali izbris vseh podatkov tega profila v primeru preveč vnosov nepravilnih gesel"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Nadzira število vnesenih nepravilnih gesel pri odklepanju zaslona in zaklene telefon ali izbriše vse podatke lastnika, če je vnesenih preveč nepravilnih gesel."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Spreminjanje zaklepanja zaslona"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Spremeni zaklepanje zaslona."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje vseh podatkov"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Izbris podatkov v tabličnem računalniku brez opozorila s ponastavitvijo na tovarniške nastavitve"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Brisanje podatkov v napravi Android TV z izvedbo ponastavitve na privzete tovarniške nastavitve brez opozorila."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Izbris podatkov informativno-razvedrilnega sistema brez opozorila s ponastavitvijo na tovarniške nastavitve"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Izbris podatkov v telefonu brez opozorila s ponastavitvijo na tovarniške nastavitve."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Izbris podatkov uporabnika"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Izbris podatkov profila"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Izbris podatkov uporabnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Izbris podatkov uporabnika v tem tabličnem računalniku brez opozorila."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Izbris podatkov uporabnika v tej napravi Android TV brez opozorila."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Izbris podatkov tega profila v informativno-razvedrilnem sistemu brez opozorila"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Izbris podatkov uporabnika v tem telefonu brez opozorila."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Nastavitev globalnega strežnika proxy za napravo"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Nastavitev globalnega strežnika proxy naprave, ki bo v uporabi, ko je pravilnik omogočen. Samo lastnik naprave lahko nastavi globalni strežnik proxy."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index d42c91e..7bd3a53 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitoro tentativat e shkyçjes së ekranit"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitoro numrin e fjalëkalimeve të shkruar gabim kur shkyç ekranin. Kyç tabletin ose fshi të gjitha të dhënat e tij, nëse shkruhen shumë fjalëkalime të pasakta."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç pajisjen tënde Android TV ose spastro të gjitha të dhënat e pajisjes sate Android TV nëse shkruhen gabim shumë fjalëkalime."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç sistemin info-argëtues ose spastro të gjitha të dhënat e tij nëse shkruhen shumë fjalëkalime të gabuara."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitoro numrin e fjalëkalimeve të shkruar gabim kur shkyç ekranin. Kyç telefonin ose fshi të gjitha të dhënat e tij, nëse shkruhen shumë fjalëkalime të pasakta."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin. Kyçe tabletin ose spastro të gjitha të dhënat e këtij përdoruesi nëse shkruhen shumë fjalëkalime të gabuara."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyçe pajisjen tënde Android TV ose spastro të gjitha të dhënat e këtij përdoruesi nëse shkruhen shumë fjalëkalime të gabuara."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin dhe kyç sistemin info-argëtues ose spastro të gjitha të dhënat e këtij profili nëse shkruhen shumë fjalëkalime të gabuara."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitoro numrin e fjalëkalimeve të shkruara gabim kur shkyç ekranin. Kyçe telefonin ose spastro të gjitha të dhënat e këtij përdoruesi nëse shkruhen shumë fjalëkalime të gabuara."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ndryshimin e kyçjes së ekranit"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ndryshon kyçjen e ekranit."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Spastrimin e të gjitha të dhënave"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Fshi të dhënat e tabletit pa paralajmërim duke kryer një rivendosje të të dhënave në gjendje fabrike."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Spastro të dhënat e pajisjes Android TV pa paralajmërim duke kryer një rivendosje të të dhënave në gjendje fabrike."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Spastro të dhënat e sistemit info-argëtues pa paralajmërim kur kryen një rivendosje të të dhënave të fabrikës."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Fshin të dhënat e telefonit pa paralajmërim, duke kryer rivendosje të të dhënave në gjendje fabrike."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Spatro të dhënat e përdoruesit"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Spastro të dhënat e profilit"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Spatro të dhënat e përdoruesit"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Spastroji të dhënat e këtij përdoruesi në këtë tablet pa paralajmërim."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Spastroji të dhënat e këtij përdoruesi në këtë pajisje Android TV pa paralajmërim."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Spastro të dhënat e këtij profili në këtë sistem info-argëtues pa paralajmërim."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Spastroji të dhënat e këtij përdoruesi në këtë telefon pa paralajmërim."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Cakto proxy-in global të pajisjes"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Cakto proxy-in global të pajisjes që të përdoret kur të aktivizohet politika. Vetëm pronari i pajisjes mund ta caktojë proxy-in global."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 04fed0c..97b233c 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -740,9 +740,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Надгледајте покушаје откључавања екрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Прати број нетачно унетих лозинки приликом откључавања екрана и закључава таблет или брише податке са таблета ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава Android TV уређај или брише све податке са Android TV уређаја ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Прати број нетачно унетих лозинки при откључавању екрана и закључава систем за инфо-забаву или брише све податке са система за инфо-забаву ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Прати број нетачно унетих лозинки при откључавању екрана и закључава телефон или брише све податке са телефона ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава таблет или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава Android TV уређај или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава систем за инфо-забаву или брише све податке овог профила ако се унесе превише нетачних лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава телефон или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Промена закључавања екрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Мења закључавање екрана."</string>
@@ -751,10 +753,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Брисање свих података"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Брисање података на таблету без упозорења ресетовањем на фабричка подешавања."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Брише податке Android TV уређаја без упозорења помоћу ресетовања на фабричка подешавања."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Брише податке на систему за инфо-забаву без упозорења ресетовањем на фабричка подешавања."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Брисање података на телефону без упозорења ресетовањем на фабричка подешавања."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Обриши податке корисника"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Брисање података профила"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Обриши податке корисника"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Брише податке овог корисника на овом таблету без упозорења."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Брише податке овог корисника на овом Android TV уређају без упозорења."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Брише податке овог профила на овом систему за инфо-забаву без упозорења."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Брише податке овог корисника на овом телефону без упозорења."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Подесите глобални прокси сервер уређаја"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Подешава глобални прокси уређаја који ће се користити док су смернице омогућене. Само власник уређаја може да подеси глобални прокси."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 2220931..740cfb9 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Övervaka försök att låsa upp skärmen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås Android TV-enheten eller rensa all data på Android TV-enheten om för många felaktiga lösenord har skrivits in."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås infotainmentsystemet eller rensa all data från infotainmentsystemet om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås surfplattan eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås Android TV-enheten eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås infotainmentsystemet eller rensa all data från profilen om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås mobilen eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ändra skärmlåset"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ändra skärmlåset."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Radera all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Radera data på Android TV-enheten utan förvarning genom att återställa standardinställningarna."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Rensa data från infotainmentsystemet utan förvarning genom att återställa standardinställningarna."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Radera användaruppgifter"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Rensa profildata"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Radera användaruppgifter"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Rensa användarens uppgifter på den här surfplattan utan förvarning."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Radera den här användarens data på den här Android TV-enheten utan förvarning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Rensa profilens data i det här infotainmentsystemet utan förvarning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Rensa användarens data på den här mobilen utan förvarning."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Ange global proxyserver"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Ange enhetens globala proxy som ska användas när policyn aktiveras. Det är bara enhetens ägare som kan ange global proxy."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 84daf98..fd1faa0 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Kuhesabu mara ambazo skrini inajaribu kufunguliwa"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Kufuatilia idadi ya manenosiri yasiyo sahihi yatakayoingizwa wakati wa kufungua skrini, na kufunga kompyuta kibao au kufuta data yote iliyomo kama manenosiri mengi yasiyo sahihi yataingizwa."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Fuatilia idadi ya manenosiri yasiyo sahihi yanayowekwa wakati wa kufungua skrini na ufunge kifaa chako cha Android TV au ufute data yake yote ikiwa mtumiaji ataweka manenosiri mengi mno yasiyo sahihi."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Fuatilia idadi ya manenosiri yasiyo sahihi yanayowekwa wakati wa kufungua skrini, na ufunge mfumo wa burudani na habari au ufute data yote kwenye mfumo wa burudani na habari ikiwa manenosiri mengi mno yasiyo sahihi yatawekwa."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Kufuatilia idadi ya manenosiri yasiyo sahihi yatakayoingizwa wakati wa kufungua skrini, na kufunga simu au kufuta data yote iliyomo kama manenosiri mengi sana yasiyo sahihi yataingizwa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Fuatilia idadi ya manenosiri yasiyo sahihi yaliyoingizwa wakati wa kufungua skrini, na ufunge kompyuta kibao au ufute data yote ya mtumiaji huyu kama ameingiza manenosiri yasiyo sahihi mara nyingi kupita kiasi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Fuatilia idadi ya manenosiri yasiyo sahihi yanayowekwa wakati wa kufungua skrini na ufunge kifaa chako cha Android TV au ufute data yote ya mtumiaji huyu ikiwa ataweka manenosiri yasiyo sahihi mara nyingi mno."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Fuatilia idadi ya manenosiri yasiyo sahihi yanayowekwa wakati wa kufungua skrini, na ufunge mfumo wa burudani na habari au ufute data yote kwenye wasifu huu ikiwa manenosiri mengi mno yasiyo sahihi yatawekwa."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Fuatilia idadi ya manenosiri yasiyo sahihi yaliyoingizwa wakati wa kufungua skrini, na ufunge simu au ufute data yote ya mtumiaji huyu kama ameingiza manenosiri yasiyo sahihi mara nyingi kupita kiasi."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Kubadilisha mbinu ya kufunga skrini"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Kubadilisha mbinu ya kufunga skrini."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Kufuta data yote"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Futa data ya kompyuta kibao bila ilani kwa kurejesha mipangilio ambayo kompyuta ilitoka nayo kiwandani."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Futa data ya kifaa chako cha Android TV bila onyo kwa kurejesha data kiliyotoka nayo kiwandani."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Futa data ya mfumo wa burudani na habari bila onyo kwa kurejesha data iliyotoka nayo kiwandani."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Kufuta data ya simu bila ilani kwa kurejesha mipangilio iliyotoka nayo kiwandani."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Futa data yote ya mtumiaji"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Futa data yote ya wasifu"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Futa data yote ya mtumiaji"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Futa data ya mtumiaji huyu iliyo kwenye kompyuta kibao hii bila ilani."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Futa data ya mtumiaji huyu iliyo kwenye kifaa hiki cha Android TV bila ilani."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Futa data yote ya wasifu kwenye mfumo huu wa burudani na habari bila onyo."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Futa data ya mtumiaji huyu iliyo kwenye simu hii bila ilani."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Weka seva mbadala ya ulimwengu kote ya kifaa"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Weka seva mbadala ya ulimwengu ya kifaa itakayotumika wakati sera imewashwa. Ni mmiliki wa kifaa pekee aneyeweza kuweka seva mbadala ya ulimwengu."</string>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 861e329..34b6a54 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,10 +51,5 @@
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<bool name="config_showUserSwitcherByDefault">true</bool>
-
- <!-- Enable dynamic keyguard positioning for large-width screens. This will cause the keyguard
- to be aligned to one side of the screen when in landscape mode. -->
- <bool name="config_enableDynamicKeyguardPositioning">true</bool>
-
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 6e4148d..0690c31 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"திரையை அன்லாக் செய்வதற்கான முயற்சிகளைக் கண்காணி"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"திரையைத் திறக்கும்போது உள்ளிட்ட தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும், மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிட்டிருந்தால், டேப்லெட்டைப் பூட்டும் அல்லது டேப்லெட்டின் எல்லா தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"திரையைத் திறக்கும்போது எத்தனை முறை தவறான கடவுச்சொற்களை உள்ளிட்டீர்கள் என்பதைக் கண்காணிக்கும், பலமுறை தவறாக உள்ளிட்டிருந்தால் Android TVயைப் பூட்டும் அல்லது Android TVயின் அனைத்துத் தரவையும் அழிக்கும்."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"திரையை அன்லாக் செய்யும்போது உள்ளிடப்படும் தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும். மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிடப்பட்டிருந்தால் இன்ஃபோடெயின்மென்ட் சிஸ்டமைப் பூட்டும் அல்லது அதன் அனைத்துத் தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"திரையைத் திறக்கும்போது உள்ளிட்ட தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும், மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிட்டிருந்தால், மொபைலைப் பூட்டும் அல்லது மொபைலின் எல்லா தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"திரையைத் திறக்கும் போது, எத்தனை முறை தவறான கடவுச்சொல்லை உள்ளிட்டீர்கள் என்பதைக் கண்காணிக்கிறது மற்றும் கடவுச்சொற்களைப் பல முறை தவறாக உள்ளிட்டால், டேப்லெட்டைப் பூட்டும் அல்லது இந்தப் பயனரின் எல்லா தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"திரையைத் திறக்கும் போது எத்தனை முறை தவறான கடவுச்சொல்லை உள்ளிட்டீர்கள் என்பதைக் கண்காணிப்பதோடு கடவுச்சொற்களைப் பல முறை தவறாக உள்ளிட்டால் Android TVயைப் பூட்டும் அல்லது இந்தப் பயனரின் அனைத்துத் தரவையும் அழிக்கும்."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"திரையை அன்லாக் செய்யும்போது உள்ளிடப்படும் தவறான கடவுச்சொற்களின் எண்ணிக்கையைக் கண்காணிக்கும். மேலும் கடவுச்சொற்கள் பலமுறை தவறாக உள்ளிடப்பட்டிருந்தால் இன்ஃபோடெயின்மென்ட் சிஸ்டமைப் பூட்டும் அல்லது இந்தச் சுயவிவரத்தின் அனைத்துத் தரவையும் அழிக்கும்."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"திரையைத் திறக்கும் போது, எத்தனை முறை தவறான கடவுச்சொல்லை உள்ளிட்டீர்கள் என்பதைக் கண்காணிக்கிறது மற்றும் கடவுச்சொற்களைப் பல முறை தவறாக உள்ளிட்டால், ஃபோனைப் பூட்டும் அல்லது இந்தப் பயனரின் எல்லா தரவையும் அழிக்கும்."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"திரைப் பூட்டை மாற்றுதல்"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"திரைப் பூட்டை மாற்றும்."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"எல்லா டேட்டாவையும் அழித்தல்"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ஆரம்பநிலைத் தரவு மீட்டமைப்பின் மூலம் எச்சரிக்கை வழங்காமல் டேப்லெட்டின் தரவை அழிக்கலாம்."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"தரவின் ஆரம்பநிலைக்கு மீட்டமைப்பதன் மூலம் எச்சரிக்கை செய்யாமல் Android TVயின் தரவை அழிக்கும்."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"தரவின் ஆரம்பநிலை மீட்டமைப்பைச் செயல்படுத்துவதன் மூலம் எச்சரிக்கை எதுவுமின்றி இன்ஃபோடெயின்மென்ட் சிஸ்டமின் தரவை அழிக்கும்."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ஆரம்பநிலைத் தரவு மீட்டமைப்பின் மூலம் எச்சரிக்கை வழங்காமல் மொபைலின் தரவை அழிக்கலாம்."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"பயனர் தரவை அழி"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"சுயவிவரத் தரவை அழித்தல்"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"பயனர் தரவை அழி"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"எச்சரிக்கை எதுவுமின்றி, டேப்லெட்டில் உள்ள இந்தப் பயனரின் தரவை அழிக்கும்."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"எச்சரிக்கை செய்யாமல் Android TVயில் உள்ள இந்தப் பயனரின் தரவை அழிக்கும்."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"எச்சரிக்கை எதுவுமின்றி இந்த இன்ஃபோடெயின்மென்ட் சிஸ்டத்திலுள்ள இந்தச் சுயவிவரத்தின் தரவை அழிக்கும்."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"எச்சரிக்கை எதுவுமின்றி, ஃபோனில் உள்ள இந்தப் பயனரின் தரவை அழிக்கும்."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"சாதன குளோபல் ப்ராக்ஸியை அமை"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"கொள்கை இயக்கப்பட்டிருக்கும்போது பயன்படுத்த வேண்டிய சாதன குளோபல் ப்ராக்ஸியை அமைக்கவும். சாதன உரிமையாளரால் மட்டுமே குளோபல் ப்ராக்ஸியை அமைக்க முடியும்."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index e8dbf3c..313ce59 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -550,7 +550,7 @@
<string name="permlab_nfc" msgid="1904455246837674977">"సమీప క్షేత్ర కమ్యూనికేషన్ను నియంత్రించడం"</string>
<string name="permdesc_nfc" msgid="8352737680695296741">"సమీప ఫీల్డ్ కమ్యూనికేషన్ (NFC) ట్యాగ్లు, కార్డులు మరియు రీడర్లతో కమ్యూనికేట్ చేయడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_disableKeyguard" msgid="3605253559020928505">"మీ స్క్రీన్ లాక్ను నిలిపివేయడం"</string>
- <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"కీలాక్ మరియు ఏదైనా అనుబంధించబడిన పాస్వర్డ్ భద్రతను నిలిపివేయడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్ను నిలిపివేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్ను మళ్లీ ప్రారంభిస్తుంది."</string>
+ <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"కీలాక్ను, అలాగే ఏదైనా అనుబంధించబడిన పాస్వర్డ్ సెక్యూరిటీని డిజేబుల్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్ను డిజేబుల్ చేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్ను మళ్లీ ఎనేబుల్ చేస్తుంది."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"స్క్రీన్ లాక్ సంక్లిష్టత రిక్వెస్ట్"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ఇది మీ స్క్రీన్ లాక్ పాస్వర్డ్ సంక్లిష్టత స్థాయి (తీవ్రంగా ఉండాలా, ఓ మోస్తరుగా ఉండాలా, తక్కువ తీవ్రంగా ఉండాలా లేదా అస్సలు తీవ్రత ఉండకూడదా) తెలుసుకోవడానికి యాప్ను అనుమతిస్తుంది, అంటే పొడుగు ఎంత ఉండాలి, ఏ రకమైన స్క్రీన్ లాక్ పధ్ధతి అనుసరించాలో సూచిస్తుంది. అలాగే, స్క్రీన్ లాక్ పాస్వర్డ్ సంక్లిష్టతను ఏ స్థాయికి సెట్ చేసుకుంటే బాగుంటుందో కూడా వినియోగదారులకు యాప్ సూచించగలదు, కానీ వినియోగదారులు నిరభ్యంతరంగా ఆ సూచనలను పట్టించుకోకుండా వారి ఇష్టం మేరకు చక్కగా సెట్ చేసుకోవచ్చు. ఇంకో ముఖ్య విషయం, స్క్రీన్ లాక్ అన్నది సాదా వచన రూపంలో నిల్వ చేయబడదు, కనుక ఖచ్చితమైన పాస్వర్డ్ ఏమిటనేది యాప్కు తెలియదు."</string>
<string name="permlab_postNotification" msgid="4875401198597803658">"నోటిఫికేషన్లను చూపండి"</string>
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"స్క్రీన్ అన్లాక్ ప్రయత్నాలను పర్యవేక్షించండి"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"టైప్ చేసిన చెల్లని పాస్వర్డ్ల సంఖ్యను పర్యవేక్షిస్తుంది. స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు, అనేక సార్లు చెల్లని పాస్వర్డ్లను టైప్ చేస్తే టాబ్లెట్ లాక్ చేయబడుతుంది లేదా టాబ్లెట్లోని మొత్తం డేటా ఎరేజ్ చేయబడుతుంది."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్లను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది, అలాగే చాలా ఎక్కువసార్లు పాస్వర్డ్లను తప్పుగా టైప్ చేసి ఉంటే మీ Android TV పరికరాన్ని లాక్ చేస్తుంది లేదా మీ Android TV డేటా మొత్తాన్ని తొలగిస్తుంది."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు, పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది. ఒకవేళ చాలా ఎక్కువ సార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే, సమాచారంతో కూడిన వినోదం సిస్టమ్ను లాక్ చేస్తుంది లేదా సమాచారంతో కూడిన వినోదం సిస్టమ్ డేటాను తొలగించి ఫ్యాక్టరీ రీసెట్ చేస్తుంది."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"టైప్ చేసిన చెల్లని పాస్వర్డ్ల సంఖ్యను పర్యవేక్షిస్తుంది. స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు, అనేక సార్లు చెల్లని పాస్వర్డ్లను టైప్ చేస్తే ఫోన్ లాక్ చేయబడుతుంది లేదా ఫోన్లోని మొత్తం డేటా ఎరేజ్ చేయబడుతుంది."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది మరియు చాలా ఎక్కువసార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే టాబ్లెట్ను లాక్ చేస్తుంది లేదా ఈ వినియోగదారు యొక్క మొత్తం డేటాను తీసివేస్తుంది."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది, చాలా ఎక్కువసార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే మీ Android TV పరికరాన్ని లాక్ చేస్తుంది లేదా ఈ వినియోగదారు యొక్క మొత్తం డేటాను తీసివేస్తుంది."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది. ఒకవేళ చాలా ఎక్కువ సార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే, సమాచారంతో కూడిన వినోదం సిస్టమ్ను లాక్ చేస్తుంది లేదా ఈ ప్రొఫైల్కు సంబంధించిన డేటాను తొలగించి ఫ్యాక్టరీ రీసెట్ చేస్తుంది."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"స్క్రీన్ను అన్లాక్ చేస్తున్నప్పుడు పాస్వర్డ్ను ఎన్నిసార్లు తప్పుగా టైప్ చేశారో పర్యవేక్షిస్తుంది మరియు చాలా ఎక్కువసార్లు పాస్వర్డ్ను తప్పుగా టైప్ చేసి ఉంటే ఫోన్ను లాక్ చేస్తుంది లేదా ఈ వినియోగదారు యొక్క మొత్తం డేటాను తీసివేస్తుంది."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"స్క్రీన్ లాక్ మార్చడానికి"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"స్క్రీన్ లాక్ని మారుస్తుంది."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"మొత్తం డేటాను ఎరేజ్ చేయడానికి"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ఫ్యాక్టరీ డేటా రీసెట్ను అమలు చేయడం ద్వారా హెచ్చరించకుండానే టాబ్లెట్ డేటాను ఎరేజ్ చేయండి."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"హెచ్చరించకుండానే మీ Android TV పరికరం డేటాను ఫ్యాక్టరీ డేటా రీసెట్ ద్వారా తొలగిస్తుంది."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ఫ్యాక్టరీ డేటా రీసెట్ను అమలు చేయడం ద్వారా, హెచ్చరిక లేకుండానే సమాచారంతో కూడిన వినోదం సిస్టమ్ డేటాను తొలగించి ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ఫ్యాక్టరీ డేటా రీసెట్ను అమలు చేయడం ద్వారా హెచ్చరించకుండానే ఫోన్ డేటాను ఎరేజ్ చేస్తుంది."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"వినియోగదారు డేటాను తీసివేయండి"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ప్రొఫైల్ డేటాను తొలగించండి"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"వినియోగదారు డేటాను తీసివేయండి"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"హెచ్చరిక లేకుండానే ఈ టాబ్లెట్లో ఈ వినియోగదారు డేటాను తీసివేస్తుంది."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"హెచ్చరిక లేకుండానే ఈ Android TV పరికరంలో ఈ వినియోగదారు డేటాను తీసివేస్తుంది."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"హెచ్చరిక లేకుండానే ఈ సమాచారంతో కూడిన వినోదం సిస్టమ్లోని ఈ ప్రొఫైల్ డేటా తొలగించబడుతుంది."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"హెచ్చరిక లేకుండానే ఈ ఫోన్లో ఈ వినియోగదారు డేటాను తీసివేస్తుంది."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"పరికరం గ్లోబల్ ప్రాక్సీని సెట్ చేయండి"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"విధానాన్ని ప్రారంభించినప్పుడు ఉపయోగించడానికి పరికర గ్లోబల్ ప్రాక్సీని సెట్ చేస్తుంది. పరికర యజమాని మాత్రమే గ్లోబల్ ప్రాక్సీని సెట్ చేయగలరు."</string>
@@ -1605,7 +1610,7 @@
<string name="data_usage_rapid_title" msgid="2950192123248740375">"అధిక మొబైల్ డేటా వినియోగం"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"మీ యాప్లు సాధారణం కంటే ఎక్కువ డేటాని ఉపయోగించాయి"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> సాధారణం కంటే ఎక్కువ డేటాని ఉపయోగించింది"</string>
- <string name="ssl_certificate" msgid="5690020361307261997">"భద్రతా సర్టిఫికెట్"</string>
+ <string name="ssl_certificate" msgid="5690020361307261997">"సెక్యూరిటీ సర్టిఫికెట్"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"ఈ సర్టిఫికెట్ చెల్లుబాటు అవుతుంది."</string>
<string name="issued_to" msgid="5975877665505297662">"దీనికి జారీ చేయబడింది:"</string>
<string name="common_name" msgid="1486334593631798443">"సాధారణ పేరు:"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 207e522..b8d8506 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ตรวจสอบจำนวนของรหัสผ่านที่พิมพ์ไม่ถูกต้องขณะปลดล็อกหน้าจอ และล็อกแท็บเล็ตหรือลบข้อมูลทั้งหมดในแท็บเล็ตถ้ามีการพิมพ์รหัสผ่านที่ไม่ถูกต้องมากเกินไป"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ตรวจสอบรหัสผ่านที่พิมพ์ไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกอุปกรณ์ Android TV หรือลบข้อมูลทั้งหมดของอุปกรณ์ Android TV หากมีการพิมพ์รหัสผ่านไม่ถูกต้องหลายครั้งเกินไป"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ตรวจสอบจำนวนครั้งที่พิมพ์รหัสผ่านไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกระบบสาระบันเทิงหรือลบข้อมูลทั้งหมดของระบบสาระบันเทิงหากพิมพ์รหัสผ่านไม่ถูกต้องบ่อยเกินไป"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ตรวจสอบจำนวนการพิมพ์รหัสผ่านที่ไม่ถูกต้องขณะปลดล็อกหน้าจอ และล็อกโทรศัพท์หรือลบข้อมูลทั้งหมดในโทรศัพท์ถ้ามีการพิมพ์รหัสผ่านที่ไม่ถูกต้องมากเกินไป"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ตรวจสอบจำนวนรหัสผ่านที่พิมพ์ไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกแท็บเล็ตหรือลบข้อมูลทั้งหมดของผู้ใช้นี้หากพิมพ์รหัสผ่านไม่ถูกต้องบ่อยเกินไป"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ตรวจสอบจำนวนรหัสผ่านที่พิมพ์ไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกอุปกรณ์ Android TV หรือลบข้อมูลทั้งหมดของผู้ใช้นี้หากพิมพ์รหัสผ่านไม่ถูกต้องหลายครั้งเกินไป"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ตรวจสอบจำนวนครั้งที่พิมพ์รหัสผ่านไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกระบบสาระบันเทิงหรือลบข้อมูลทั้งหมดของโปรไฟล์นี้หากพิมพ์รหัสผ่านไม่ถูกต้องบ่อยเกินไป"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ตรวจสอบจำนวนรหัสผ่านที่พิมพ์ไม่ถูกต้องเวลาปลดล็อกหน้าจอ และล็อกโทรศัพท์หรือลบข้อมูลทั้งหมดของผู้ใช้นี้หากพิมพ์รหัสผ่านไม่ถูกต้องบ่อยเกินไป"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"เปลี่ยนการล็อกหน้าจอ"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"เปลี่ยนการล็อกหน้าจอ"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"ลบข้อมูลทั้งหมด"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ลบข้อมูลของแท็บเล็ตโดยไม่มีการเตือน ด้วยการดำเนินการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ลบข้อมูลของอุปกรณ์ Android TV โดยไม่มีการเตือนด้วยการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ดำเนินการรีเซ็ตข้อมูลเป็นค่าเริ่มต้นเพื่อลบข้อมูลของระบบสาระบันเทิงโดยไม่มีการเตือน"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ลบข้อมูลโทรศัพท์โดยไม่มีการเตือน ด้วยการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"ลบข้อมูลผู้ใช้"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ลบข้อมูลโปรไฟล์"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ลบข้อมูลผู้ใช้"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ลบข้อมูลของผู้ใช้นี้ในแท็บเล็ตเครื่องนี้โดยไม่มีการเตือน"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ลบข้อมูลของผู้ใช้นี้ในอุปกรณ์ Android TV เครื่องนี้โดยไม่มีการเตือน"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ลบข้อมูลของโปรไฟล์ดังกล่าวในระบบสาระบันเทิงนี้โดยไม่มีการเตือน"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ลบข้อมูลของผู้ใช้นี้ในโทรศัพท์เครื่องนี้โดยไม่มีการเตือน"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"ตั้งค่าพร็อกซีส่วนกลางของอุปกรณ์"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ตั้งค่าพร็อกซีส่วนกลางของอุปกรณ์ที่จะใช้ขณะที่เปิดใช้นโยบายอยู่ เฉพาะเจ้าของอุปกรณ์เท่านั้นที่สามารถตั้งค่าพร็อกซีส่วนกลาง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index e99f0c4..30350a5 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Subaybayan ang bilang ng mga hindi tamang password na na-type kapag ina-unlock ang screen, at i-lock ang tablet o burahin ang lahat ng data ng tablet kung masyadong maraming hindi tamang password ang na-type."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Subaybayan ang dami ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang Android TV device o burahin ang lahat ng data ng Android TV device kung masyadong maraming maling password ang na-type."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Subaybayan ang bilang ng mga maling password kapag ina-unlock ang screen, at i-lock ang infotainment system o burahin ang lahat ng data ng infotainment system kung masyadong maraming maling password ang nata-type."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang telepono o burahin ang lahat ng data ng telepono kung masyadong maraming maling password ang na-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang tablet o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang nata-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Subaybayan ang dami ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang iyong Android TV device o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang na-type."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang infotainment system o burahin ang lahat ng data ng profile na ito kung masyadong maraming maling password ang nata-type."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Subaybayan ang bilang ng mga maling password na na-type kapag ina-unlock ang screen, at i-lock ang telepono o burahin ang lahat ng data ng user na ito kung masyadong maraming maling password ang nata-type."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Palitan ang screen lock"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Palitan ang screen lock."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Burahin ang lahat ng data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Burahin ang data ng tablet nang walang babala sa pamamagitan ng pagsasagawa ng pag-reset ng factory data."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Burahin ang data ng iyong Android TV device nang walang babala sa pamamagitan ng pagsasagawa ng pag-reset ng factory data."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Burahin ang data ng infotainment system nang walang babala sa pamamagitan ng pagsasagawa ng pag-reset ng factory data."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Burahin ang data ng telepono nang walang babala sa pamamagitan ng pagsasagawa ng pag-reset ng factory data."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Burahin ang data ng user"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Burahin ang data ng profile"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Burahin ang data ng user"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Burahin ang data ng user na ito sa tablet na ito nang walang babala."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Burahin ang data ng user na ito sa Android TV device na ito nang walang babala."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Burahin ang data ng profile na ito sa infotainment system na ito nang walang babala."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Burahin ang data ng user na ito sa teleponong ito nang walang babala."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Itakda ang pandaigdigang proxy ng device"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Itakda ang pandaigdigang proxy ng device na gagamitin habang naka-enable ang patakaran. Ang may-ari ng device lang ang makakapagtakda sa pandaigdigang proxy."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b3f15a2..e676c89 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekran kilidini açma denemelerini izle"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Ekran kilidini açarken yapılan yanlış şifre girme denemelerini izler ve çok fazla sayıda yanlış şifre girme denemesi yapılmışsa tableti kilitler veya tabletteki tüm verileri siler."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip eder ve çok fazla sayıda hatalı şifre girildiğinde Android TV cihazınızı kilitler veya Android TV cihazınızın tüm verilerini siler."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Ekran kilidi açılırken girilen hatalı şifre sayısı takip edilir ve çok fazla sayıda hatalı şifre girildiğinde bilgi-eğlence sistemi kilitlenir ya da bilgi-eğlence sistemindeki tüm veriler silinir."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Ekran kilidini açarken yapılan yanlış şifre girişi denemelerini izler ve çok sayıda yanlış şifre girişi denemesi yapılmışsa telefonu kilitler veya telefonun tüm verilerini siler."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip edin ve çok fazla sayıda hatalı şifre girildiğinde tableti kilitleyin veya söz konusu kullanıcının tüm verilerini silin."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip edin ve çok fazla sayıda hatalı şifre girildiğinde Android TV cihazınızı kilitleyin veya söz konusu kullanıcının tüm verilerini silin."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Ekran kilidi açılırken girilen hatalı şifre sayısı takip edilir ve çok fazla sayıda hatalı şifre girildiğinde tablet kilitlenir veya söz konusu kullanıcının tüm verileri silinir."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip edin ve çok fazla sayıda hatalı şifre girildiğinde telefonu kilitleyin veya söz konusu kullanıcının tüm verilerini silin."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ekran kilidini değiştirme"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ekran kilidini değiştirir."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Tüm verileri silme"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek tabletteki verileri uyarıda bulunmadan siler."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek Android TV cihazınızdaki verileri uyarıda bulunmadan siler."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek bilgi-eğlence sistemindeki veriler uyarıda bulunmadan silinir."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek telefondaki verileri uyarıda bulunmadan siler."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Kullanıcı verilerini sil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profil verilerini silme"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Kullanıcı verilerini sil"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Uyarı yapmadan bu kullanıcının bu tabletteki verilerini silin."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Uyarı yapmadan bu kullanıcının bu Android TV cihazındaki verilerini siler."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bu bilgi-eğlence sistemindeki bu profilin verileri uyarıda bulunmadan silinir."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Uyarı yapmadan bu kullanıcının bu telefondaki verilerini siler."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Cihaz genelinde geçerli proxy\'i ayarla"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Politika etkin olduğunda kullanılacak cihaz genelinde geçerli proxy\'yi ayarlar. Genel proxy\'yi yalnızca cihaz sahibi ayarlayabilir."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index e16d100..18c5600 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -743,9 +743,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Відстежувати спроби розблокування екрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Відстежувати кількість неправильних паролів, введених під час розблокування екрана, і блокувати планшетний ПК або стирати всі його дані, якщо введено забагато неправильних паролів."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана. Блокуйте пристрій Android TV або стирайте всі його дані, якщо пароль введено неправильно забагато разів."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана, і блокуйте інформаційно-розважальну систему або видаляйте всі її дані, якщо пароль введено неправильно забагато разів."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Відстежувати кількість неправильних паролів, введених під час розблокування екрана, і блокувати телефон або стирати всі його дані, якщо введено забагато неправильних паролів."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана. Блокуйте планшет або стирайте всі його дані, якщо пароль введено неправильно забагато разів."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана. Блокуйте пристрій Android TV або стирайте всі дані користувача, якщо пароль введено неправильно забагато разів."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана, і блокуйте інформаційно-розважальну систему або видаляйте всі дані цього профілю, якщо пароль введено неправильно забагато разів."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Відстежуйте кількість неправильних паролів, введених під час розблокування екрана. Блокуйте телефон або стирайте всі його дані, якщо пароль введено неправильно забагато разів."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Змінити спосіб розблокування екрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Змінити спосіб розблокування екрана."</string>
@@ -754,10 +756,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Видалити всі дані"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Стирати дані планшетного ПК без попередження, відновлюючи заводські налаштування."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Видаляйте дані пристрою Android TV без попередження шляхом відновлення заводських налаштувань."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Видаляйте всі дані інформаційно-розважальної системи без попередження, відновлюючи заводські налаштування."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Стирати дані телефона без попередження, відновивши заводські налаштування."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Видалення даних користувача"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Видалити всі дані профілю"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Видалення даних користувача"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Видаляйте дані користувача на цьому планшеті без попередження."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Видаляйте дані користувача на цьому пристрої Android TV без попередження."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Видаляйте всі дані цієї інформаційно-розважальної системи без попередження."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Видаляйте дані користувача на цьому телефоні без попередження."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Установ. глоб. проксі пристрою"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Використовуйте глобальний проксі-сервер пристрою, коли це правило ввімкнено. Налаштувати глобальний проксі-сервер може лише власник пристрою."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 9621b95..fdb8ddd 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"اسکرین غیر مقفل کرنے کی کوششیں مانیٹر کریں"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کیے گئے غلط پاس ورڈز کی تعداد مانیٹر کریں اور ٹیبلیٹ کو مقفل کریں یا اگر کافی زیادہ غلط پاس ورڈز ٹائپ کیے گئے ہیں تو ٹیبلیٹ کا سبھی ڈیٹا صاف کریں۔"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد پر نگاہ رکھیں اور اگر بہت زیادہ غلط پاسورڈز ٹائپ کیے جاتے ہیں تو اپنے Android TV آلہ کو مقفل کردیں یا اپنے Android TV آلہ کے ڈیٹا کو مٹادیں۔"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کیے گئے غلط پاس ورڈز کی تعداد مانیٹر کریں۔ اور معلوماتی انٹرٹینمنٹ سسٹم کو مقفل کریں یا اگر کافی زیادہ غلط پاس ورڈز ٹائپ کیے گئے ہیں تو معلوماتی انٹرٹینمنٹ سسٹم کا سبھی ڈیٹا صاف کریں۔"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کیے گئے غلط پاس ورڈز کی تعداد مانیٹر کریں اور فون کو مقفل کریں یا اگر کافی زیادہ غلط پاس ورڈز ٹائپ کیے گئے ہیں تو فون کا سبھی ڈیٹا صاف کریں۔"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد پر نگاہ رکھیں اور اگر بہت زیادہ غلط پاس ورڈز ٹائپ کیے جاتے ہیں تو ٹیبلٹ کو مقفل کریں یا اس صارف کا سبھی ڈیٹا ہٹائیں۔"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد پر نگاہ رکھیں اور اگر بہت زیادہ غلط پاس ورڈز ٹائپ کیے جاتے ہیں تو اپنے Android TV آلہ کو مقفل کریں یا اس صارف کا سبھی ڈیٹا ہٹائیں۔"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد مانیٹر کریں اور معلوماتی انٹرٹینمنٹ سسٹم کو مقفل کریں اگر بہت زیادہ غلط پاس ورڈز ٹائپ کیے جاتے ہیں تو اس پروفائل کا سبھی ڈیٹا ہٹائیں۔"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"اسکرین کو غیر مقفل کرتے وقت ٹائپ کردہ غلط پاس ورڈز کی تعداد پر نگاہ رکھیں اور اگر بہت زیادہ غلط پاس ورڈز ٹائپ کیے جاتے ہیں تو فون کو مقفل کریں یا اس صارف کا سبھی ڈیٹا ہٹائیں۔"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"اسکرین لاک تبدیل کریں"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"اسکرین لاک تبدیل کریں۔"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"سبھی ڈیٹا صاف کریں"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"فیکٹری ڈیٹا ری سیٹ انجام دے کر وارننگ کے بغیر ٹیبلٹ کا ڈیٹا مٹائیں۔"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"فیکٹری ڈیٹا ری سیٹ کو انجام دے کر انتباہ کیے بغیر اپنے Android TV آلہ کا ڈیٹا مٹائیں۔"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"فیکٹری ڈیٹا ری سیٹ کو انجام دے کر وارننگ کے بغیر معلوماتی انٹرٹینمنٹ سسٹم کا ڈیٹا صاف کریں۔"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"فیکٹری ڈیٹا ری سیٹ انجام دے کر وارننگ کے بغیر فون کا ڈیٹا مٹائیں۔"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"صارف کا ڈیٹا ہٹائیں"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"پروفائل ڈیٹا صاف کریں"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"صارف کا ڈیٹا ہٹائیں"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"وارننگ کے بغیر اس ٹیبلٹ پر موجود اس صارف کا ڈیٹا ہٹائیں۔"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"انتباہ کے بغیر اس Android TV آلہ پر اس صارف کا ڈیٹا ہٹائیں۔"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"وارننگ کے بغیر اس معلوماتی انٹرٹینمنٹ سسٹم پر اس پروفائل کا ڈیٹا صاف کریں۔"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"وارننگ کے بغیر اس فون پر موجود اس صارف کا ڈیٹا ہٹائیں۔"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"آلہ کی عالمی پراکسی سیٹ کریں"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"پالیسی فعال ہونے پر آلہ کی عالمی پراکسی کو استعمال کیے جانے کیلئے سیٹ کریں۔ صرف آلہ کا مالک ہی عالمی پراکسی سیٹ کر سکتا ہے۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 3d98dab..73cc496 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranni qulfdan chiqarishga urinishlarni nazorat qilish"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Ekranni qulfini ochishda parolni kiritishga urinishlarni kuzatib boradi va agar parol bir necha marta noto‘g‘ri kiritilsa, planshetni qulflaydi yoki undagi ma’lumotlarni o‘chirib tashlaydi."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Ekran qulfini ochishda kiritilgan xato parollar sonini kuzatib boradi va agar parol juda koʻp marta xato kiritilsa, Android TV qurilmasini qulflaydi yoki undagi barcha maʼlumotlarni oʻchirib tashlaydi."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Ekranni qulfini ochishda parolni kiritishga urinishlarni kuzatib boradi va agar parol bir necha marta xato kiritilsa, axborot-hordiq tizimini qulflaydi yoki undagi maʼlumotlarni oʻchirib tashlaydi."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Ekranni qulfini ochishda parolni kiritishga urinishlarni kuzatib boradi va agar parol bir necha marta noto‘g‘ri kiritilsa, telefonni qulflaydi yoki undagi ma’lumotlarni o‘chirib tashlaydi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Ekran qulfini ochishda kiritilgan noto‘g‘ri parollar sonini kuzatib boradi va agar parol juda ko‘p marta noto‘g‘ri kiritilsa, planshetni qulflaydi yoki undagi barcha ma’lumotlarni o‘chirib tashlaydi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Ekran qulfini ochishda kiritilgan xato parollar sonini kuzatib boradi va agar parol juda koʻp marta xato kiritilsa, Android TV qurilmangizni qulflaydi yoki undagi barcha maʼlumotlarni oʻchirib tashlaydi."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Ekran qulfini ochishda parolni kiritishga urinishlarni kuzatib boradi va agar parol bir necha marta xato kiritilsa, axborot-hordiq tizimini qulflaydi yoki undagi barcha profil maʼlumotlarini oʻchirib tashlaydi."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Ekran qulfini ochishda kiritilgan noto‘g‘ri parollar sonini kuzatib boradi va agar parol juda ko‘p marta noto‘g‘ri kiritilsa, telefonni qulflaydi yoki undagi barcha ma’lumotlarni o‘chirib tashlaydi."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Ekran qulfini almashtirish"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Ekran qulfini almashtiradi."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Hamma narsani tozalash"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Planshetdagi hamma narsani tozalab tashlaydi va uning sozlamalarini asliga qaytaradi."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Android TV qurilmangizdagi hamma narsani tozalab tashlaydi va uning sozlamalarini asliga qaytaradi."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Zavod sozlamalarini qayta tiklash orqali axbirot-hordiq tizimi maʼlumotlarini ogohlantirishsiz oʻchirib tashlansin."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Telefondagi hamma narsani tozalab tashlaydi va uning sozlamalarini asliga qaytaradi."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Foydalanuvchi ma’lumotlarini o‘chirish"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Profil maʼlumotlarini tozalash"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Foydalanuvchi ma’lumotlarini o‘chirish"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ushbu planshetdagi foydalanuvchi ma’lumotlarini ogohlantirishsiz o‘chirib tashlaydi."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Bu Android TV qurilmangizdagi foydalanuvchi maʼlumotlarini ogohlantirishsiz oʻchirib tashlaydi."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Bu profil maʼlumotlari ogohlantirishsiz oʻchirib tashlansin."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ushbu telefondagi foydalanuvchi ma’lumotlarini ogohlantirishsiz o‘chirib tashlaydi."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Qurilmaga global proksi o‘rnatish"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Qoida faollashtirilgan vaqtda ishlatiladigan qurilmaning global proksi-serverini o‘rnatadi. Faqat qurilma egasi global proksi-serverini o‘rnatishi mumkin."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 1367485..d47b4f4 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Giám sát những lần thử mở khóa màn hình"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Theo dõi số lần nhập mật khẩu không chính xác khi mở khóa màn hình và khóa máy tính bảng hoặc xóa tất cả dữ liệu của máy tính bảng nếu có quá nhiều lần nhập mật khẩu không chính xác."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Giám sát số lần nhập mật khẩu không chính xác khi mở khóa màn hình, đồng thời khóa thiết bị Android TV hoặc xóa tất cả dữ liệu trên thiết bị Android TV nếu bạn nhập mật khẩu sai quá nhiều lần."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Theo dõi số lần nhập sai mật khẩu khi mở khoá màn hình và khoá hệ thống thông tin giải trí hoặc xoá tất cả dữ liệu của hệ thống này nếu có quá nhiều lần nhập sai mật khẩu."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Theo dõi số lần nhập mật khẩu không chính xác khi mở khóa màn hình và khóa điện thoại hoặc xóa tất cả dữ liệu của điện thoại nếu có quá nhiều lần nhập mật khẩu không chính xác."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Giám sát số lần nhập sai mật khẩu khi mở khóa màn hình và khóa máy tính bảng hoặc xóa tất cả dữ liệu của người dùng này nếu nhập sai mật khẩu quá nhiều lần."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Giám sát số lần nhập mật khẩu không chính xác khi mở khóa màn hình, đồng thời khóa thiết bị Android TV hoặc xóa tất cả dữ liệu của người dùng này nếu bạn nhập mật khẩu sai quá nhiều lần."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Theo dõi số lần nhập sai mật khẩu khi mở khoá màn hình và khoá hệ thống thông tin giải trí hoặc xoá tất cả dữ liệu của hồ sơ này nếu có quá nhiều lần nhập sai mật khẩu."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Giám sát số lần nhập sai mật khẩu khi mở khóa màn hình và khóa điện thoại hoặc xóa tất cả dữ liệu của người dùng này nếu nhập sai mật khẩu quá nhiều lần."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Thay đổi phương thức khóa màn hình"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Thay đổi phương thức khóa màn hình."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Xóa tất cả dữ liệu"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Xóa dữ liệu trên máy tính bảng mà không cần cảnh báo, bằng cách thực hiện thiết lập lại dữ liệu ban đầu."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Xóa dữ liệu trên thiết bị Android TV mà không cảnh báo bằng cách thiết lập lại dữ liệu ban đầu."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Xoá dữ liệu của hệ thống thông tin giải trí mà không cảnh báo bằng cách đặt lại dữ liệu về trạng thái ban đầu."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Xóa dữ liệu trên điện thoại mà không cần cảnh báo, bằng cách thiết lập lại dữ liệu ban đầu."</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Xóa dữ liệu người dùng"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Xoá dữ liệu hồ sơ"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Xóa dữ liệu người dùng"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Xóa dữ liệu của người dùng trên máy tính bảng này mà không cảnh báo."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Xóa dữ liệu của người dùng này trên thiết bị Android TV mà không cảnh báo."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Xoá dữ liệu của hồ sơ này trên hệ thống thông tin giải trí này mà không cảnh báo."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Xóa dữ liệu của người dùng trên điện thoại này mà không cảnh báo."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Đặt proxy chung của điện thoại"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Đặt proxy chung của thiết bị được sử dụng trong khi chính sách bật. Chỉ chủ sở hữu thiết bị mới có thể đặt proxy chung."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 2dd9d6b..17ce14b 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"监控屏幕解锁尝试次数"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"监视在解锁屏幕时输错密码的次数,如果输错次数过多,则锁定平板电脑或清除其所有数据。"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"监控用户在解锁屏幕时输错密码的次数;如果用户输错密码的次数超出上限,系统就会锁定 Android TV 设备或清空 Android TV 设备上的所有数据。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定信息娱乐系统或清除信息娱乐系统上的所有数据。"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"监视在解锁屏幕时输错密码的次数,如果输错次数过多,则锁定手机或清除其所有数据。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定平板电脑或清空此用户的所有数据。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"监控用户在解锁屏幕时输错密码的次数;如果用户输错密码的次数超出上限,系统就会锁定 Android TV 设备或清空该用户的所有数据。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定信息娱乐系统或清除此个人资料的所有数据。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"监控在解锁屏幕时输错密码的次数,并在输错次数过多时锁定手机或清空此用户的所有数据。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"更改锁屏方式"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"更改锁屏方式。"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"清除所有数据"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"恢复出厂设置时,系统会在不发出警告的情况下清除平板电脑上的数据。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"不事先发出警告就以恢复出厂设置的方式清空 Android TV 设备中的数据。"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"在不发出警告的情况下,通过恢复出厂设置来清除信息娱乐系统上的数据。"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"恢复出厂设置时,系统会在不发出警告的情况下清除手机上的数据。"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"清空用户数据"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"清除个人资料数据"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"清空用户数据"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"清空此用户在这台平板电脑上的数据,而不事先发出警告。"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"不事先发出警告就清空此用户在这台 Android TV 设备上的数据。"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"在不发出警告的情况下,从该信息娱乐系统中清除此个人资料的所有数据。"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"清空此用户在这部手机上的数据,而不事先发出警告。"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"设置设备全局代理"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"设置在规范启用时要使用的设备全局代理。只有设备所有者才能设置全局代理。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 7a220a4..35a11b2 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"監視為螢幕解鎖時輸入錯誤密碼的次數;如果輸入錯誤密碼的次數過多,則會鎖定平板電腦或清除平板電腦的所有資料。"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"監察螢幕解鎖時錯誤輸入密碼的次數,並在錯誤輸入密碼的次數過多時,將 Android TV 裝置上鎖或清除裝置上的所有資料。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"監察螢幕解鎖時錯誤輸入密碼的次數,如果錯誤輸入密碼的次數過多,即鎖定資訊娛樂系統或清除資訊娛樂系統中的所有資料。"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"監視為螢幕解鎖時輸入錯誤密碼的次數,如果輸入錯誤密碼的次數過多,則會鎖定手機或清除手機的所有資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"監察螢幕解鎖時錯誤輸入密碼的次數,如果錯誤輸入密碼的次數過多,即鎖定平板電腦或清除這個使用者的資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"監察螢幕解鎖密碼輸入錯誤的次數,並在密碼輸入錯誤的次數超出上限時將 Android TV 裝置上鎖,或清除該使用者的所有資料。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"監察螢幕解鎖時錯誤輸入密碼的次數,如果錯誤輸入密碼的次數過多,即鎖定資訊娛樂系統或清除這個設定檔的所有資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"監察螢幕解鎖時錯誤輸入密碼的次數,如果錯誤輸入密碼的次數過多,即鎖定手機或清除這個使用者的資料。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"變更螢幕鎖定"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"變更螢幕鎖定。"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"清除所有資料"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"重設平板電腦為原廠設定,在不提出警告的情況下直接清除平板電腦的資料。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"系統可將 Android TV 裝置回復原廠設定,在沒有警告的情況下清除裝置的資料。"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"回復資訊娛樂系統為原廠設定,在不提出警告的情況下直接清除資訊娛樂系統的資料。"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"重設手機為原廠設定,在不提出警告的情況下直接清除手機的資料。"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"清除使用者資料"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"清除個人檔案資料"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"清除使用者資料"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"清除這個使用者在這部平板電腦上的資料而不作警告。"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"在沒有警告的情況下,清除這位使用者在此 Android TV 裝置上的資料。"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"在不提出警告的情況下直接清除此設定檔在資訊娛樂系統上的資料。"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"清除這個使用者在這部手機上的資料而不作警告。"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"設定裝置的全域代理伺服器"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"設定政策啟用時所要使用的裝置全域代理伺服器,只有裝置擁有者可以設定全域代理伺服器。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6df610c..42f0586 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"監控螢幕解鎖時密碼輸入錯誤的次數;如果密碼輸入錯誤的次數過多,則會鎖住平板電腦或全部清除平板電腦中的資料。"</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"監控螢幕解鎖密碼輸入錯誤的次數。如果輸入錯誤的次數超過上限,系統會將 Android TV 裝置鎖定,或清除 Android TV 裝置的所有資料。"</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"監控密碼輸入錯誤的次數。解鎖螢幕時,如果密碼輸入錯誤次數過多,系統就會鎖住資訊娛樂系統或清除資訊娛樂系統的所有資料。"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"監控螢幕解鎖時密碼輸入錯誤的次數;如果密碼輸入錯誤的次數過多,則會鎖住手機或清除手機的所有資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"監控螢幕解鎖密碼輸入錯誤的次數;如果輸入錯誤的次數超過上限,系統會將平板電腦鎖定,或將這個使用者的資料全部清除。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"監控螢幕解鎖密碼輸入錯誤的次數。如果輸入錯誤的次數超過上限,系統會將 Android TV 裝置鎖定,或清除這位使用者的所有資料。"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"監控螢幕解鎖時的密碼輸入錯誤次數。如果錯誤次數過多,系統就會鎖住資訊娛樂系統或清除這個設定檔的所有資料。"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"監控螢幕解鎖密碼輸入錯誤的次數;如果輸入錯誤的次數超過上限,系統會將手機鎖定,或將這個使用者的資料全部清除。"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"變更鎖定螢幕方式"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"變更鎖定螢幕方式。"</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"清除所有資料"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"恢復原廠設定,不提出警告就直接清除平板電腦的資料。"</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"在沒有事先警告的情況下讓系統恢復原廠設定,清除 Android TV 裝置上的資料。"</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"不事先警告就透過恢復原廠設定的方式清除資訊娛樂系統的資料。"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"恢復原廠設定,不提出警告就直接清除手機的資料。"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"清除使用者資料"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"清除設定檔資料"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"清除使用者資料"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"將這個使用者的資料從這台平板電腦中清除,而不事先發出警告。"</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"在沒有事先警告的情況下,將這位使用者的資料從這部 Android TV 裝置中清除。"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"不事先警告就從這個資訊娛樂系統上清除這個設定檔的資料。"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"將這個使用者的資料從這支手機中清除,而不事先發出警告。"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"設定裝置全域 Proxy"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"設定政策啟用時要使用的裝置全域 Proxy。只有裝置擁有者可以設定全域 Proxy。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 518a45f..3f7e37d 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -737,9 +737,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Qapha imizamo yokuvula isikrini sakho"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Bheka inani lamaphasiwedi angafanele athayishiwe uma kuvulwa iskrini bese kuvalwa ithebhulethi noma kususwe yonke idatha yethebhulethi uma kubhalwe amaphasiwedi amaningi angalungile."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Ngamela inombolo yamaphasiwedi angalungile athayiphiwe uma kuvulwa isikrini, nokukhiya idivayisi yakho ye-Android TV noma ukususa yonke idatha yedivayisi yakho ye-Android TV uma amaphasiwedi amaningi angalungile athayiphiwe."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Qaphela inombolo yamaphasiwedi angalungile athayiphiwe uma kuvulwa isikrini, uphinde ukhiye isistimu ye-infotainment noma usule yonke idatha yesistimu ye-infotainment uma ngabe kuthayiphwa amaphasiwedi amaningi angalungile."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Bheka isibalo samaphasiwedi ngalungile afakiwe uma uvula iskrini bese uvala ucingo noma ususe yonke imininingwane yocingo uma kubhalwe amaphasiwedi amaningi angalungile."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Qaphela inombolo yamaphasiwedi angalungile athayiphwe uma kuvulwa isikrini, uphinde ukhiye ithebulethi noma usule yonke idatha yalo msebenzisi uma ngabe kuthayiphwe amaphasiwedi amaningi kakhulu angalungile."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Qaphela inombolo yamaphasiwedi angalungile athayiphiwe uma kuvulwa isikrini, uphinde ukhiye idivayisi yakho ye-Android TV noma usule yonke idatha yalo msebenzisi uma ngabe kuthayiphwe amaphasiwedi amaningi kakhulu angalungile."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Qaphela inombolo yamaphasiwedi angalungile athayiphwe uma kuvulwa isikrini, uphinde ukhiye isistimu ye-infotainment noma usule yonke idatha yaleli phrofayela uma kuthayiphwe amaphasiwedi amaningi kakhulu angalungile."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Qaphela inombolo yamaphasiwedi angalungile athayiphiwe uma kuvulwa isikrini, uphinde ukhiye ifoni noma usule yonke idatha yalo msebenzisi uma ngabe kuthayiphwe amaphasiwedi amaningi kakhulu angalungile."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Guqula ukukhiya isikrini"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Guqula ukukhiya isikrini."</string>
@@ -748,10 +750,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Sula yonke idatha"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Sula idatha yethebhulethi ngaphandle kwesaziso, ngokwenza ukusetha kabusha kwemboni."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Sula idatha yakho yedivayisi ye-Android TV ngaphandle kokuxwayisa ngokwenza ukusetha kwedatha kwefekthri."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Sula idatha isistimu ye-infotainment ngaphandle kokuxwayisa ngokwenza ukusetha kabusha kwasekuqaleni kwedatha."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Sula idatha yefoni ngaphandle kwesixwayiso, ngokwenza ukuhlela kabusha idatha yemboni"</string>
- <string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"Sula idatha yomsebenzisi"</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Sula idatha yephrofayela"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Sula idatha yomsebenzisi"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Sula idatha yalo msebenzisi kule thebulethi ngaphandle kwesexwayiso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Sula le datha yomsebenzisi kule divayisi ye-Android TV ngaphandle kwesexwayiso."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Sula idatha yale phrofayela kule sistimu ye-infotainment ngaphandle kwesixwayiso."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Sula idatha yalo msebenzisi kule foni ngaphandle kwesexwayiso."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Misa ummelelii jikelele yedivaysi"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Setha ummeleli womhlaba wonke wedivayisi ozosetshenziswa ngenkathi inqubomgomo inikwe amandla. Ngumnikazi wedivayisi kuphela ongasetha ummeleli womhlaba wonke."</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 94717b1..fe58114 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2303,6 +2303,36 @@
<attr name="authorities" />
</declare-styleable>
+ <!-- The <code>sdk-library</code> tag declares that this apk is providing itself
+ as an SDK library for other applications to use. Any app can declare an SDK library and there
+ can be only one SDK library per package. These SDK libraries are updatable, multiple major
+ versions can be installed at the same time, and an app depends on a specific version.
+ Other apks can link to it with the {@link #AndroidManifestUsesSdkLibrary uses-sdk-library} tag.
+
+ <p>This appears as a child tag of the {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestSdkLibrary" parent="AndroidManifestApplication">
+ <!-- Required public name of the SDK library, which other components and packages will use
+ when referring to this SDK library. This is a string using Java-style scoping to ensure
+ it is unique.
+ Both name and version should typically form the apk's package name: name_versionMajor. -->
+ <attr name="name" />
+ <!-- Required major version of the SDK library. -->
+ <attr name="versionMajor" format="integer" />
+ </declare-styleable>
+
+
+ <!-- The <code>uses-sdk-library</code> specifies a shared <strong>SDK</strong> library that this
+ package requires to be present on the device.
+
+ <p>This appears as a child tag of the {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestUsesSdkLibrary" parent="AndroidManifestApplication">
+ <!-- Required name of the SDK library you use. -->
+ <attr name="name" />
+ <!-- Specify which major version of the SDK library you use. -->
+ <attr name="versionMajor" format="integer" />
+ <!-- The SHA-256 digest of the SDK library signing certificate. -->
+ <attr name="certDigest" format="string" />
+ </declare-styleable>
<!-- The <code>static-library</code> tag declares that this apk is providing itself
as a static shared library for other applications to use. Any app can declare such
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2c60fbd8..ee21d91 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1048,9 +1048,16 @@
0 - Nothing
1 - Toggle theater mode setting
2 - Brightness boost
+ 3 - Launch target activity defined by config_doublePressOnPowerTargetActivity
+ if available
-->
<integer name="config_doublePressOnPowerBehavior">0</integer>
+ <!-- Activity name for the default target activity to be launched. Note that
+ config_doublePressOnPowerBehavior must be set to 3 for this to work. [DO NOT TRANSLATE]
+ -->
+ <string name="config_doublePressOnPowerTargetActivity" translatable="false"></string>
+
<!-- Control the behavior when the user triple presses the power button.
0 - Nothing
1 - Toggle theater mode setting
@@ -1483,6 +1490,30 @@
<integer-array name="config_autoBrightnessLevels">
</integer-array>
+ <!-- Array of light sensor lux values to define our levels for auto backlight brightness
+ support whilst in idle mode.
+ The N entries of this array define N + 1 control points as follows:
+ (1-based arrays)
+
+ Point 1: (0, value[1]): lux <= 0
+ Point 2: (level[1], value[2]): 0 < lux <= level[1]
+ Point 3: (level[2], value[3]): level[2] < lux <= level[3]
+ ...
+ Point N+1: (level[N], value[N+1]): level[N] < lux
+
+ The control points must be strictly increasing. Each control point
+ corresponds to an entry in the brightness backlight values arrays.
+ For example, if lux == level[1] (first element of the levels array)
+ then the brightness will be determined by value[2] (second element
+ of the brightness values array).
+
+ Spline interpolation is used to determine the auto-brightness
+ backlight values for lux levels between these control points.
+
+ Must be overridden in platform specific overlays -->
+ <integer-array name="config_autoBrightnessLevelsIdle">
+ </integer-array>
+
<!-- Timeout (in milliseconds) after which we remove the effects any user interactions might've
had on the brightness mapping. This timeout doesn't start until we transition to a
non-interactive display policy so that we don't reset while users are using their devices,
@@ -1506,6 +1537,10 @@
<integer-array name="config_autoBrightnessLcdBacklightValues_doze">
</integer-array>
+ <!-- Enables idle screen brightness mode on this device.
+ If this is true, config_autoBrightnessDisplayValuesNitsIdle must be defined. -->
+ <bool name="config_enableIdleScreenBrightnessMode">false</bool>
+
<!-- Array of desired screen brightness in nits corresponding to the lux values
in the config_autoBrightnessLevels array. As with config_screenBrightnessMinimumNits and
config_screenBrightnessMaximumNits, the display brightness is defined as the measured
@@ -1522,6 +1557,13 @@
<array name="config_autoBrightnessDisplayValuesNits">
</array>
+ <!-- Array of desired screen brightness in nits for idle screen brightness mode.
+ This array should meet the same requirements as config_autoBrightnessDisplayValuesNits.
+ This array also corresponds to the lux values given in config_autoBrightnessLevelsIdle.
+ In order to activate this mode, config_enableIdleScreenBrightnessMode must be true. -->
+ <array name="config_autoBrightnessDisplayValuesNitsIdle">
+ </array>
+
<!-- Array of output values for button backlight corresponding to the luX values
in the config_autoBrightnessLevels array. This array should have size one greater
than the size of the config_autoBrightnessLevels array.
@@ -1767,6 +1809,13 @@
provider services. -->
<string name="config_secondaryLocationTimeZoneProviderPackageName" translatable="false"></string>
+ <!-- Whether the time zone detection logic supports fall back from geolocation suggestions to
+ telephony suggestions temporarily in certain circumstances. Reduces time zone detection
+ latency during some scenarios like air travel. Only useful when both geolocation and
+ telephony time zone detection are supported on a device.
+ See com.android.server.timezonedetector.TimeZoneDetectorStrategy for more information. -->
+ <bool name="config_supportTelephonyTimeZoneFallback" translatable="false">true</bool>
+
<!-- Whether to enable network location overlay which allows network location provider to be
replaced by an app at run-time. When disabled, only the
config_networkLocationProviderPackageName package will be searched for network location
@@ -4899,12 +4948,6 @@
button. -->
<integer name="config_mashPressVibrateTimeOnPowerButton">0</integer>
- <!-- Control the behavior when the user presses the power button 5 times.
- 0 - Nothing
- 1 - Launch panic button gesture
- -->
- <integer name="config_mashPressOnPowerBehavior">0</integer>
-
<!-- Whether or not to enable the binder heavy hitter watcher by default -->
<bool name="config_defaultBinderHeavyHitterWatcherEnabled">false</bool>
@@ -4943,9 +4986,10 @@
the app in the letterbox mode. -->
<item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item>
- <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
- min between device bottom corner radii will be used instead. -->
- <integer name="config_letterboxActivityCornersRadius">-1</integer>
+ <!-- Corners radius for activity presented the letterbox mode. Values < 0 enable rounded
+ corners with radius equal to min between device bottom corner radii. Default 0 value turns
+ off rounded corners logic in LetterboxUiController. -->
+ <integer name="config_letterboxActivityCornersRadius">0</integer>
<!-- Blur radius for the Option 3 in R.integer.config_letterboxBackgroundType. Values < 0 are
ignored and 0 is used. -->
@@ -5044,10 +5088,6 @@
<!-- Whether to select voice/data/sms preference without user confirmation -->
<bool name="config_voice_data_sms_auto_fallback">false</bool>
- <!-- Whether to enable dynamic keyguard positioning for wide screen devices (e.g. only using
- half of the screen, to be accessible using only one hand). -->
- <bool name="config_enableDynamicKeyguardPositioning">false</bool>
-
<!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot -->
<bool name="config_allow_pin_storage_for_unattended_reboot">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index faa9902..0495122 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -463,6 +463,7 @@
<java-symbol type="integer" name="config_shortPressOnStemPrimaryBehavior" />
<java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" />
<java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" />
+ <java-symbol type="string" name="config_doublePressOnPowerTargetActivity" />
<java-symbol type="integer" name="config_windowOutsetBottom" />
<java-symbol type="integer" name="db_connection_pool_size" />
<java-symbol type="integer" name="db_journal_size_limit" />
@@ -1902,6 +1903,7 @@
<java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
<java-symbol type="array" name="config_autoBrightnessLcdBacklightValues_doze" />
<java-symbol type="array" name="config_autoBrightnessLevels" />
+ <java-symbol type="array" name="config_autoBrightnessLevelsIdle" />
<java-symbol type="array" name="config_ambientThresholdLevels" />
<java-symbol type="array" name="config_ambientBrighteningThresholds" />
<java-symbol type="array" name="config_ambientDarkeningThresholds" />
@@ -2237,6 +2239,7 @@
<java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
<java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
<java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
+ <java-symbol type="bool" name="config_supportTelephonyTimeZoneFallback" />
<java-symbol type="bool" name="config_autoResetAirplaneMode" />
<java-symbol type="string" name="config_notificationAccessConfirmationActivity" />
<java-symbol type="bool" name="config_killableInputMethods" />
@@ -3788,7 +3791,9 @@
<java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" />
<java-symbol type="drawable" name="ic_logout" />
+ <java-symbol type="bool" name="config_enableIdleScreenBrightnessMode" />
<java-symbol type="array" name="config_autoBrightnessDisplayValuesNits" />
+ <java-symbol type="array" name="config_autoBrightnessDisplayValuesNitsIdle" />
<java-symbol type="array" name="config_screenBrightnessBacklight" />
<java-symbol type="array" name="config_screenBrightnessNits" />
@@ -4322,8 +4327,6 @@
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
- <java-symbol type="bool" name="config_enableDynamicKeyguardPositioning" />
-
<java-symbol type="attr" name="colorAccentPrimary" />
<java-symbol type="attr" name="colorAccentSecondary" />
<java-symbol type="attr" name="colorAccentTertiary" />
@@ -4614,4 +4617,6 @@
<java-symbol type="array" name="config_roundedCornerBottomRadiusAdjustmentArray" />
<java-symbol type="bool" name="config_secondaryBuiltInDisplayIsRound" />
<java-symbol type="array" name="config_builtInDisplayIsRoundArray" />
+
+ <java-symbol type="integer" name="config_mashPressVibrateTimeOnPowerButton" />
</resources>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index 409025b..8eb6ebc 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -587,7 +587,8 @@
final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
mContext.registerReceiver(receiver, filter);
- assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+ assertEquals(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE),
+ BluetoothStatusCodes.SUCCESS);
boolean success = false;
try {
success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
@@ -637,7 +638,8 @@
final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
mContext.registerReceiver(receiver, filter);
- assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+ assertEquals(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE),
+ BluetoothStatusCodes.SUCCESS);
boolean success = false;
try {
success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 207671e..60d48b2 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -533,7 +533,7 @@
}
@Override
- public void scheduleCrash(String s, int i) throws RemoteException {
+ public void scheduleCrash(String s, int i, Bundle extras) throws RemoteException {
}
@Override
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java
index 1ff88f7..5ba9059 100644
--- a/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java
@@ -19,9 +19,11 @@
import static com.google.common.truth.Truth.assertThat;
import android.app.Person;
+import android.platform.test.annotations.Presubmit;
import org.junit.Test;
+@Presubmit
public class AppSearchPersonTest {
@Test
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
index 21eb44a..969357f 100644
--- a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
@@ -21,14 +21,18 @@
import android.app.Person;
import android.content.ComponentName;
import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.Set;
+@Presubmit
public class AppSearchShortcutInfoTest {
+ @Ignore("b/208375334")
@Test
public void testBuildShortcutAndGetValue() {
final String category =
diff --git a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
index 9c03e1a..34ff8b0 100644
--- a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
@@ -16,16 +16,16 @@
package android.content.pm;
-import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
-import android.view.inputmethod.InputMethodInfo;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+@Presubmit
public class AppsQueryHelperTests extends AndroidTestCase {
private AppsQueryHelper mAppsQueryHelper;
diff --git a/core/tests/coretests/src/android/content/pm/ComponentTest.java b/core/tests/coretests/src/android/content/pm/ComponentTest.java
index f31f0b5..2342386 100644
--- a/core/tests/coretests/src/android/content/pm/ComponentTest.java
+++ b/core/tests/coretests/src/android/content/pm/ComponentTest.java
@@ -23,6 +23,7 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import androidx.test.filters.MediumTest;
@@ -48,6 +49,7 @@
* would fix this.
*/
@Suppress // Failing.
+@Presubmit
public class ComponentTest extends AndroidTestCase {
private PackageManager mPackageManager;
diff --git a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
index 1c703ab..bfbb4a0 100644
--- a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
+++ b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import androidx.test.filters.MediumTest;
@@ -25,6 +26,7 @@
import java.io.InputStream;
import java.util.Arrays;
+@Presubmit
public class LimitedLengthInputStreamTest extends AndroidTestCase {
private final byte[] TEST_STRING1 = "This is a test".getBytes();
diff --git a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
index 1ddd753..963925a 100644
--- a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
+++ b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import androidx.test.filters.LargeTest;
@@ -29,6 +30,7 @@
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
+@Presubmit
@LargeTest
public class MacAuthenticatedInputStreamTest extends AndroidTestCase {
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index b627619..947da0b 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -34,6 +35,7 @@
import java.util.List;
import java.util.UUID;
+@Presubmit
public class PackageHelperTests extends AndroidTestCase {
private static final boolean localLOGV = true;
public static final String TAG = "PackageHelperTests";
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
index 3757712..d505492 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
@@ -24,12 +24,14 @@
import android.content.pm.PackageManager.Property;
import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class PackageManagerPropertyTests {
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 7f53776..c2519ca 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -50,6 +50,7 @@
import android.os.RemoteException;
import android.os.StatFs;
import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.system.ErrnoException;
@@ -85,6 +86,7 @@
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
+@Presubmit
public class PackageManagerTests extends AndroidTestCase {
private static final boolean localLOGV = true;
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
index e852f98..61a3a11 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageParserCacheHelper.WriteHelper;
import android.os.Bundle;
import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -29,6 +30,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PackageParserCacheHelperTest {
diff --git a/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java b/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java
new file mode 100644
index 0000000..2986d61
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.util.function.Function.identity;
+
+import android.content.pm.PackagePartitions.SystemPartition;
+import android.os.SystemProperties;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class PackagePartitionsTest {
+
+ @Test
+ public void testPackagePartitionsFingerprint() {
+ final ArrayList<SystemPartition> partitions = PackagePartitions.getOrderedPartitions(
+ identity());
+ final String[] properties = new String[partitions.size() + 1];
+ for (int i = 0; i < partitions.size(); i++) {
+ final String name = partitions.get(i).getName();
+ properties[i] = "ro." + name + ".build.fingerprint";
+ }
+ properties[partitions.size()] = "ro.build.fingerprint";
+
+ assertThat(SystemProperties.digestOf(properties)).isEqualTo(PackagePartitions.FINGERPRINT);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index 8874525..01907fb 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.LargeTest;
@@ -27,6 +28,7 @@
import java.util.Collections;
import java.util.List;
+@Presubmit
@LargeTest
public class ParceledListSliceTest extends TestCase {
diff --git a/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java b/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java
index 606e81d..5879092 100644
--- a/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -28,6 +29,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public final class PermissionInfoTest {
private static final String KNOWN_CERT_DIGEST_1 =
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index e04d903..fa4952e1 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -21,6 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import android.util.AttributeSet;
import android.util.SparseArray;
@@ -45,6 +46,7 @@
/**
* Tests for {@link android.content.pm.RegisteredServicesCache}
*/
+@Presubmit
@LargeTest
public class RegisteredServicesCacheTest extends AndroidTestCase {
private static final int U0 = 0;
diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java
index 19458da..fb0a435 100644
--- a/core/tests/coretests/src/android/content/pm/SignatureTest.java
+++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java
@@ -16,10 +16,13 @@
package android.content.pm;
+import android.platform.test.annotations.Presubmit;
+
import androidx.test.filters.LargeTest;
import junit.framework.TestCase;
+@Presubmit
@LargeTest
public class SignatureTest extends TestCase {
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index cf7e5c66..d522349 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.util.PackageUtils;
@@ -38,6 +39,7 @@
import java.util.Set;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SigningDetailsTest {
diff --git a/core/tests/coretests/src/android/content/pm/TEST_MAPPING b/core/tests/coretests/src/android/content/pm/TEST_MAPPING
new file mode 100644
index 0000000..15e04d1
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/TEST_MAPPING
@@ -0,0 +1,40 @@
+{
+ "presubmit-large": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.content.pm."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.content.pm."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Postsubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
index e7cd02d..c058280 100644
--- a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
@@ -17,11 +17,15 @@
package android.content.pm;
import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.LargeTest;
+import org.junit.Ignore;
+
import java.util.Random;
+@Presubmit
@LargeTest
public class VerifierDeviceIdentityTest extends android.test.AndroidTestCase {
private static final long TEST_1 = 0x7A5F00FF5A55AAA5L;
@@ -151,6 +155,7 @@
}
}
+ @Ignore("b/208373220")
public void testVerifierDeviceIdentity_Generate_MinValue() {
VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_MINVALUE);
@@ -162,6 +167,7 @@
+ " should be the same", id1, id2);
}
+ @Ignore("b/208373220")
public void testVerifierDeviceIdentity_Generate_Random() {
VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1);
diff --git a/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt b/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
index 9ad63ad..19f470a 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
+++ b/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
@@ -18,6 +18,7 @@
import android.content.pm.PackageManager
import android.os.Build
+import android.platform.test.annotations.Presubmit
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assume.assumeFalse
@@ -34,6 +35,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import java.io.IOException
+@Presubmit
class ParseInputAndResultTest {
companion object {
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationTest.java b/core/tests/coretests/src/android/os/CombinedVibrationTest.java
index 06b5d18..508856b 100644
--- a/core/tests/coretests/src/android/os/CombinedVibrationTest.java
+++ b/core/tests/coretests/src/android/os/CombinedVibrationTest.java
@@ -125,6 +125,12 @@
VibrationEffect.createOneShot(1, 1)).getDuration());
assertEquals(-1, CombinedVibration.createParallel(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration());
+ assertEquals(-1, CombinedVibration.createParallel(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .getDuration());
assertEquals(Long.MAX_VALUE, CombinedVibration.createParallel(
VibrationEffect.createWaveform(
new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0)).getDuration());
@@ -175,6 +181,83 @@
}
@Test
+ public void testIsHapticFeedbackCandidateMono() {
+ assertTrue(CombinedVibration.createParallel(
+ VibrationEffect.createOneShot(1, 1)).isHapticFeedbackCandidate());
+ assertTrue(CombinedVibration.createParallel(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).isHapticFeedbackCandidate());
+ assertTrue(CombinedVibration.createParallel(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .isHapticFeedbackCandidate());
+ // Too long to be classified as a haptic feedback.
+ assertFalse(CombinedVibration.createParallel(
+ VibrationEffect.createOneShot(10_000, 1)).isHapticFeedbackCandidate());
+ // Repeating vibrations should not be classified as a haptic feedback.
+ assertFalse(CombinedVibration.createParallel(
+ VibrationEffect.createWaveform(new long[]{1}, new int[]{1}, 0))
+ .isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidateStereo() {
+ assertTrue(CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.createOneShot(1, 1))
+ .addVibrator(2, VibrationEffect.createWaveform(new long[]{6}, new int[]{1}, -1))
+ .combine()
+ .isHapticFeedbackCandidate());
+ assertTrue(CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .combine()
+ .isHapticFeedbackCandidate());
+ // Repeating vibrations should not be classified as a haptic feedback.
+ assertFalse(CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2, VibrationEffect.createWaveform(new long[]{1}, new int[]{1}, 0))
+ .combine()
+ .isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidateSequential() {
+ assertTrue(CombinedVibration.startSequential()
+ .addNext(1, VibrationEffect.createOneShot(10, 10), 10)
+ .addNext(2, VibrationEffect.createWaveform(new long[]{5}, new int[]{1}, -1))
+ .combine()
+ .isHapticFeedbackCandidate());
+ assertTrue(CombinedVibration.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(2,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL)
+ .compose())
+ .combine()
+ .isHapticFeedbackCandidate());
+ // Repeating vibrations should not be classified as a haptic feedback.
+ assertFalse(CombinedVibration.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(2, VibrationEffect.createWaveform(new long[]{1}, new int[]{1}, 0))
+ .combine()
+ .isHapticFeedbackCandidate());
+ // Too many effects to be classified as a haptic feedback.
+ assertFalse(CombinedVibration.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
+ .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK))
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_THUD))
+ .combine()
+ .isHapticFeedbackCandidate());
+ }
+
+ @Test
public void testHasVibratorMono_returnsTrueForAnyVibrator() {
CombinedVibration effect = CombinedVibration.createParallel(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index 6cbfffc..781564b 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -17,6 +17,7 @@
package android.os;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -312,8 +313,106 @@
assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(1)).getAmplitude());
}
+
+ @Test
+ public void testDuration() {
+ assertEquals(1, VibrationEffect.createOneShot(1, 1).getDuration());
+ assertEquals(-1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration());
+ assertEquals(-1,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose()
+ .getDuration());
+ assertEquals(6, VibrationEffect.createWaveform(
+ new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1).getDuration());
+ assertEquals(Long.MAX_VALUE, VibrationEffect.createWaveform(
+ new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0).getDuration());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_repeatingEffects_notCandidates() {
+ assertFalse(VibrationEffect.createWaveform(
+ new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0).isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_longEffects_notCandidates() {
+ assertFalse(VibrationEffect.createOneShot(1500, 255).isHapticFeedbackCandidate());
+ assertFalse(VibrationEffect.createWaveform(
+ new long[]{200, 200, 700}, new int[]{1, 2, 3}, -1).isHapticFeedbackCandidate());
+ assertFalse(VibrationEffect.startWaveform()
+ .addRamp(1, 500)
+ .addStep(1, 200)
+ .addRamp(0, 500)
+ .build()
+ .isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_shortEffects_areCandidates() {
+ assertTrue(VibrationEffect.createOneShot(500, 255).isHapticFeedbackCandidate());
+ assertTrue(VibrationEffect.createWaveform(
+ new long[]{100, 200, 300}, new int[]{1, 2, 3}, -1).isHapticFeedbackCandidate());
+ assertTrue(VibrationEffect.startWaveform()
+ .addRamp(1, 300)
+ .addStep(1, 200)
+ .addRamp(0, 300)
+ .build()
+ .isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_longCompositions_notCandidates() {
+ assertFalse(VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL)
+ .compose()
+ .isHapticFeedbackCandidate());
+
+ assertFalse(VibrationEffect.startComposition()
+ .addEffect(VibrationEffect.createOneShot(1500, 255))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .compose()
+ .isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_shortCompositions_areCandidates() {
+ assertTrue(VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose()
+ .isHapticFeedbackCandidate());
+
+ assertTrue(VibrationEffect.startComposition()
+ .addEffect(VibrationEffect.createOneShot(100, 255))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .compose()
+ .isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() {
+ assertFalse(VibrationEffect.get(
+ VibrationEffect.RINGTONES[1]).isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_prebakedNotRingtoneConstants_areCandidates() {
+ assertTrue(VibrationEffect.get(VibrationEffect.EFFECT_CLICK).isHapticFeedbackCandidate());
+ assertTrue(VibrationEffect.get(VibrationEffect.EFFECT_THUD).isHapticFeedbackCandidate());
+ assertTrue(VibrationEffect.get(VibrationEffect.EFFECT_TICK).isHapticFeedbackCandidate());
+ }
+
private Resources mockRingtoneResources() {
- return mockRingtoneResources(new String[] {
+ return mockRingtoneResources(new String[]{
RINGTONE_URI_1,
RINGTONE_URI_2,
RINGTONE_URI_3
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index 0ac8f08..bdd76a5 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -223,7 +223,7 @@
public void vibrate_withAudioAttributes_createsVibrationAttributesWithSameUsage() {
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(
- AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY).build();
+ AudioAttributes.USAGE_VOICE_COMMUNICATION).build();
mVibratorSpy.vibrate(effect, audioAttributes);
@@ -235,7 +235,7 @@
assertEquals(VibrationAttributes.USAGE_COMMUNICATION_REQUEST,
vibrationAttributes.getUsage());
// Keeps original AudioAttributes usage to be used by the VibratorService.
- assertEquals(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY,
+ assertEquals(AudioAttributes.USAGE_VOICE_COMMUNICATION,
vibrationAttributes.getAudioUsage());
}
@@ -250,17 +250,4 @@
VibrationAttributes vibrationAttributes = captor.getValue();
assertEquals(new VibrationAttributes.Builder().build(), vibrationAttributes);
}
-
- @Test
- public void vibrate_withoutAudioAttributesAndLongEffect_hasUnknownUsage() {
- mVibratorSpy.vibrate(VibrationEffect.createOneShot(10_000, 255));
-
- ArgumentCaptor<VibrationAttributes> captor = ArgumentCaptor.forClass(
- VibrationAttributes.class);
- verify(mVibratorSpy).vibrate(anyInt(), anyString(), any(), isNull(), captor.capture());
-
- VibrationAttributes vibrationAttributes = captor.getValue();
- assertEquals(VibrationAttributes.USAGE_UNKNOWN, vibrationAttributes.getUsage());
- assertEquals(AudioAttributes.USAGE_UNKNOWN, vibrationAttributes.getAudioUsage());
- }
}
diff --git a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
index de80812..a0e1f43 100644
--- a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -17,6 +17,7 @@
package android.os.vibrator;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
@@ -105,4 +106,49 @@
VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
assertSame(prebaked, prebaked.scale(0.5f));
}
+
+ @Test
+ public void testDuration() {
+ assertEquals(-1, new PrebakedSegment(
+ VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .getDuration());
+ assertEquals(-1, new PrebakedSegment(
+ VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .getDuration());
+ assertEquals(-1, new PrebakedSegment(
+ VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .getDuration());
+ assertEquals(-1, new PrebakedSegment(
+ VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .getDuration());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_prebakedConstants_areCandidates() {
+ assertTrue(new PrebakedSegment(
+ VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .isHapticFeedbackCandidate());
+ assertTrue(new PrebakedSegment(
+ VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .isHapticFeedbackCandidate());
+ assertTrue(new PrebakedSegment(
+ VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .isHapticFeedbackCandidate());
+ assertTrue(new PrebakedSegment(
+ VibrationEffect.EFFECT_HEAVY_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .isHapticFeedbackCandidate());
+ assertTrue(new PrebakedSegment(
+ VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .isHapticFeedbackCandidate());
+ assertTrue(new PrebakedSegment(
+ VibrationEffect.EFFECT_TEXTURE_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .isHapticFeedbackCandidate());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() {
+ assertFalse(new PrebakedSegment(
+ VibrationEffect.RINGTONES[1], true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ .isHapticFeedbackCandidate());
+ }
}
diff --git a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
index 538655b..a690553 100644
--- a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -127,4 +127,28 @@
assertEquals(0f, initial.scale(1.5f).scale(2 / 3f).getScale(), TOLERANCE);
assertEquals(0f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE);
}
+
+ @Test
+ public void testDuration() {
+ assertEquals(-1, new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 10).getDuration());
+ assertEquals(-1, new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100).getDuration());
+ assertEquals(-1, new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_SPIN, 1, 0).getDuration());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_returnsTrue() {
+ assertTrue(new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 10).isHapticFeedbackCandidate());
+ assertTrue(new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 10).isHapticFeedbackCandidate());
+ assertTrue(new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10).isHapticFeedbackCandidate());
+ assertTrue(new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_THUD, 1, 10).isHapticFeedbackCandidate());
+ assertTrue(new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_SPIN, 1, 10).isHapticFeedbackCandidate());
+ }
}
diff --git a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
index 174b4a7..5f80d2a 100644
--- a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
@@ -125,4 +125,16 @@
assertEquals(0.35f, initial.scale(0.8f).getStartAmplitude(), TOLERANCE);
assertEquals(0.5f, initial.scale(0.8f).scale(1.25f).getStartAmplitude(), TOLERANCE);
}
+
+ @Test
+ public void testDuration() {
+ assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_returnsTrue() {
+ // A single ramp segment duration is not checked here, but contributes to the effect known
+ // duration checked in VibrationEffect implementations.
+ assertTrue(new RampSegment(0.5f, 1, 0, 0, 5_000).isHapticFeedbackCandidate());
+ }
}
diff --git a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
index 79529b8..fdce86a 100644
--- a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
@@ -141,4 +141,16 @@
assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scale(1.5f).getAmplitude(),
TOLERANCE);
}
+
+ @Test
+ public void testDuration() {
+ assertEquals(5, new StepSegment(0, 0, 5).getDuration());
+ }
+
+ @Test
+ public void testIsHapticFeedbackCandidate_returnsTrue() {
+ // A single step segment duration is not checked here, but contributes to the effect known
+ // duration checked in VibrationEffect implementations.
+ assertTrue(new StepSegment(0, 0, 5_000).isHapticFeedbackCandidate());
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 33c6a4b..e689b5d3 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -45,7 +45,6 @@
import com.google.common.base.Throwables;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,13 +81,6 @@
mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher);
}
- @After
- public void tearDown() {
- // Make sure we're recycling all of our window and node infos.
- mAccessibilityCache.clear();
- AccessibilityInteractionClient.getInstance().clearCache();
- }
-
@Test
public void testEmptyCache_returnsNull() {
assertNull(mAccessibilityCache.getNode(0, 0));
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index 7e1e7f4..3e061d2 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -17,6 +17,9 @@
package android.view.accessibility;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -40,6 +43,8 @@
@RunWith(AndroidJUnit4.class)
public class AccessibilityInteractionClientTest {
private static final int MOCK_CONNECTION_ID = 0xabcd;
+ private static final int MOCK_CONNECTION_OTHER_ID = 0xabce;
+
private MockConnection mMockConnection = new MockConnection();
@Mock private AccessibilityCache mMockCache;
@@ -47,8 +52,8 @@
@Before
public void setUp() {
initMocks(this);
- AccessibilityInteractionClient.setCache(mMockCache);
- AccessibilityInteractionClient.addConnection(MOCK_CONNECTION_ID, mMockConnection);
+ AccessibilityInteractionClient.addConnection(
+ MOCK_CONNECTION_ID, mMockConnection, /*initializeCache=*/true);
}
/**
@@ -58,6 +63,7 @@
*/
@Test
public void findA11yNodeInfoByA11yId_whenBypassingCache_doesntTouchCache() {
+ AccessibilityInteractionClient.setCache(MOCK_CONNECTION_ID, mMockCache);
final int windowId = 0x1234;
final long accessibilityNodeId = 0x4321L;
AccessibilityNodeInfo nodeFromConnection = AccessibilityNodeInfo.obtain();
@@ -71,6 +77,42 @@
verifyZeroInteractions(mMockCache);
}
+ @Test
+ public void getCache_differentConnections_returnsDifferentCaches() {
+ MockConnection mOtherMockConnection = new MockConnection();
+ AccessibilityInteractionClient.addConnection(
+ MOCK_CONNECTION_OTHER_ID, mOtherMockConnection, /*initializeCache=*/true);
+
+ AccessibilityCache firstCache = AccessibilityInteractionClient.getCache(MOCK_CONNECTION_ID);
+ AccessibilityCache secondCache = AccessibilityInteractionClient.getCache(
+ MOCK_CONNECTION_OTHER_ID);
+ assertNotEquals(firstCache, secondCache);
+ }
+
+ @Test
+ public void getCache_addConnectionWithoutCache_returnsNullCache() {
+ // Need to first remove from process cache
+ AccessibilityInteractionClient.removeConnection(MOCK_CONNECTION_OTHER_ID);
+
+ MockConnection mOtherMockConnection = new MockConnection();
+ AccessibilityInteractionClient.addConnection(
+ MOCK_CONNECTION_OTHER_ID, mOtherMockConnection, /*initializeCache=*/false);
+
+ AccessibilityCache cache = AccessibilityInteractionClient.getCache(
+ MOCK_CONNECTION_OTHER_ID);
+ assertNull(cache);
+ }
+
+ @Test
+ public void getCache_removeConnection_returnsNull() {
+ AccessibilityCache cache = AccessibilityInteractionClient.getCache(MOCK_CONNECTION_ID);
+ assertNotNull(cache);
+
+ AccessibilityInteractionClient.removeConnection(MOCK_CONNECTION_ID);
+ cache = AccessibilityInteractionClient.getCache(MOCK_CONNECTION_ID);
+ assertNull(cache);
+ }
+
private static class MockConnection extends AccessibilityServiceConnectionImpl {
AccessibilityNodeInfo mInfoToReturn;
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index a6e351d..52cb9f3 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -24,16 +24,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.content.res.Configuration;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
-import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -59,17 +56,14 @@
public class WindowContextControllerTest {
private WindowContextController mController;
@Mock
- private IWindowManager mMockWms;
- @Mock
private WindowTokenClient mMockToken;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mController = new WindowContextController(mMockToken, mMockWms);
+ mController = new WindowContextController(mMockToken);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
- doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
- anyInt(), anyInt(), any());
+ doReturn(true).when(mMockToken).attachToDisplayArea(anyInt(), anyInt(), any());
}
@Test(expected = IllegalStateException.class)
@@ -81,10 +75,10 @@
}
@Test
- public void testDetachIfNeeded_NotAttachedYet_DoNothing() throws Exception {
+ public void testDetachIfNeeded_NotAttachedYet_DoNothing() {
mController.detachIfNeeded();
- verify(mMockWms, never()).detachWindowContextFromWindowContainer(any());
+ verify(mMockToken, never()).detachFromWindowContainerIfNeeded();
}
@Test
@@ -93,8 +87,6 @@
null /* options */);
assertThat(mController.mAttachedToDisplayArea).isTrue();
- verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY),
- eq(false) /* shouldReportConfigChange */);
mController.detachIfNeeded();
diff --git a/data/etc/com.android.intentresolver.xml b/data/etc/com.android.intentresolver.xml
index 0f1c467..f4e94ad 100644
--- a/data/etc/com.android.intentresolver.xml
+++ b/data/etc/com.android.intentresolver.xml
@@ -18,5 +18,6 @@
<privapp-permissions package="com.android.intentresolver">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/displayconfig/Android.bp b/data/etc/displayconfig/Android.bp
new file mode 100644
index 0000000..b2a45d2
--- /dev/null
+++ b/data/etc/displayconfig/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+prebuilt_etc {
+ name: "default_television.xml",
+ sub_dir: "displayconfig",
+ src: "default_television.xml",
+}
diff --git a/data/etc/displayconfig/OWNERS b/data/etc/displayconfig/OWNERS
new file mode 100644
index 0000000..6ce1ee4
--- /dev/null
+++ b/data/etc/displayconfig/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/display/OWNERS
diff --git a/data/etc/displayconfig/default_television.xml b/data/etc/displayconfig/default_television.xml
new file mode 100644
index 0000000..2540f59
--- /dev/null
+++ b/data/etc/displayconfig/default_television.xml
@@ -0,0 +1,24 @@
+<displayConfiguration>
+ <densityMap>
+ <density>
+ <height>480</height>
+ <width>720</width>
+ <density>120</density>
+ </density>
+ <density>
+ <height>720</height>
+ <width>1280</width>
+ <density>213</density>
+ </density>
+ <density>
+ <height>1080</height>
+ <width>1920</width>
+ <density>320</density>
+ </density>
+ <density>
+ <height>2160</height>
+ <width>3840</width>
+ <density>640</density>
+ </density>
+ </densityMap>
+</displayConfiguration>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 103b836..f83f401 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -422,6 +422,11 @@
<permission name="android.permission.MANAGE_NOTIFICATIONS"/>
<!-- Permission required for CompanionDeviceManager CTS test. -->
<permission name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS" />
+ <permission name="android.permission.MANAGE_COMPANION_DEVICES" />
+ <permission name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING" />
+ <permission name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION" />
+ <permission name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
+ <permission name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
<!-- Permission required for testing registering pull atom callbacks. -->
<permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
<!-- Permission required for testing system audio effect APIs. -->
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 8ccf02c..6bfbd8d 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -889,6 +889,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
},
+ "-1145384901": {
+ "message": "shouldWaitAnimatingExit: isTransition: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1142279614": {
"message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
"level": "VERBOSE",
@@ -1273,6 +1279,12 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-743856570": {
+ "message": "shouldWaitAnimatingExit: isAnimating: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-743431900": {
"message": "Configuration no differences in %s",
"level": "VERBOSE",
@@ -1777,6 +1789,12 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
+ "-208825711": {
+ "message": "shouldWaitAnimatingExit: isWallpaperTarget: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-198463978": {
"message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
"level": "VERBOSE",
@@ -2989,6 +3007,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
},
+ "1087494661": {
+ "message": "Clear window stuck on animatingExit status: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"1088929964": {
"message": "onLockTaskPackagesUpdated: starting new locktask task=%s",
"level": "DEBUG",
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 0f356a6..2f56b18 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -74,6 +74,10 @@
* getBounds().right + getBounds().getWidth() * #getExtraInsetFraction(),
* getBounds().bottom + getBounds().getHeight() * #getExtraInsetFraction())
* </pre>
+ *
+ * <p>An alternate drawable can be specified using <code><monochrome></code> tag which can be
+ * drawn in place of the two (background and foreground) layers. This drawable is tinted
+ * according to the device or surface theme.
*/
public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback {
@@ -120,6 +124,7 @@
*/
private static final int BACKGROUND_ID = 0;
private static final int FOREGROUND_ID = 1;
+ private static final int MONOCHROME_ID = 2;
/**
* State variable that maintains the {@link ChildDrawable} array.
@@ -188,6 +193,18 @@
*/
public AdaptiveIconDrawable(Drawable backgroundDrawable,
Drawable foregroundDrawable) {
+ this(backgroundDrawable, foregroundDrawable, null);
+ }
+
+ /**
+ * Constructor used to dynamically create this drawable.
+ *
+ * @param backgroundDrawable drawable that should be rendered in the background
+ * @param foregroundDrawable drawable that should be rendered in the foreground
+ * @param monochromeDrawable an alternate drawable which can be tinted per system theme color
+ */
+ public AdaptiveIconDrawable(@Nullable Drawable backgroundDrawable,
+ @Nullable Drawable foregroundDrawable, @Nullable Drawable monochromeDrawable) {
this((LayerState)null, null);
if (backgroundDrawable != null) {
addLayer(BACKGROUND_ID, createChildDrawable(backgroundDrawable));
@@ -195,6 +212,9 @@
if (foregroundDrawable != null) {
addLayer(FOREGROUND_ID, createChildDrawable(foregroundDrawable));
}
+ if (monochromeDrawable != null) {
+ addLayer(MONOCHROME_ID, createChildDrawable(monochromeDrawable));
+ }
}
/**
@@ -227,9 +247,8 @@
state.mSourceDrawableId = Resources.getAttributeSetSourceResId(attrs);
final ChildDrawable[] array = state.mChildren;
- for (int i = 0; i < state.mChildren.length; i++) {
- final ChildDrawable layer = array[i];
- layer.setDensity(deviceDensity);
+ for (int i = 0; i < array.length; i++) {
+ array[i].setDensity(deviceDensity);
}
inflateLayers(r, parser, attrs, theme);
@@ -286,6 +305,18 @@
return mLayerState.mChildren[BACKGROUND_ID].mDrawable;
}
+
+ /**
+ * Returns the monochrome version of this drawable. Callers can use a tinted version of
+ * this drawable instead of the original drawable on surfaces stressing user theming.
+ *
+ * @return the monochrome drawable
+ */
+ @Nullable
+ public Drawable getMonochrome() {
+ return mLayerState.mChildren[MONOCHROME_ID].mDrawable;
+ }
+
@Override
protected void onBoundsChange(Rect bounds) {
if (bounds.isEmpty()) {
@@ -316,9 +347,6 @@
for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
final ChildDrawable r = mLayerState.mChildren[i];
- if (r == null) {
- continue;
- }
final Drawable d = r.mDrawable;
if (d == null) {
continue;
@@ -359,14 +387,11 @@
if (mLayersShader == null) {
mCanvas.setBitmap(mLayersBitmap);
mCanvas.drawColor(Color.BLACK);
- for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
- if (mLayerState.mChildren[i] == null) {
- continue;
- }
- final Drawable dr = mLayerState.mChildren[i].mDrawable;
- if (dr != null) {
- dr.draw(mCanvas);
- }
+ if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
+ mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(mCanvas);
+ }
+ if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
+ mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(mCanvas);
}
mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint.setShader(mLayersShader);
@@ -480,12 +505,18 @@
continue;
}
String tagName = parser.getName();
- if (tagName.equals("background")) {
- childIndex = BACKGROUND_ID;
- } else if (tagName.equals("foreground")) {
- childIndex = FOREGROUND_ID;
- } else {
- continue;
+ switch (tagName) {
+ case "background":
+ childIndex = BACKGROUND_ID;
+ break;
+ case "foreground":
+ childIndex = FOREGROUND_ID;
+ break;
+ case "monochrome":
+ childIndex = MONOCHROME_ID;
+ break;
+ default:
+ continue;
}
final ChildDrawable layer = new ChildDrawable(state.mDensity);
@@ -941,7 +972,7 @@
static class LayerState extends ConstantState {
private int[] mThemeAttrs;
- final static int N_CHILDREN = 2;
+ static final int N_CHILDREN = 3;
ChildDrawable[] mChildren;
// The density at which to render the drawable and its children.
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 7354c90..b843589 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -79,7 +79,7 @@
* mask using {@code setId(..., android.R.id.mask)} or an existing mask layer
* may be replaced using {@code setDrawableByLayerId(android.R.id.mask, ...)}.
* <pre>
- * <code><!-- A red ripple masked against an opaque rectangle. --/>
+ * <code><!-- A red ripple masked against an opaque rectangle. -->
* <ripple android:color="#ffff0000">
* <item android:id="@android:id/mask"
* android:drawable="@android:color/white" />
@@ -92,12 +92,12 @@
* If no mask layer is set, the ripple effect is masked against the composite
* of the child layers.
* <pre>
- * <code><!-- A green ripple drawn atop a black rectangle. --/>
+ * <code><!-- A green ripple drawn atop a black rectangle. -->
* <ripple android:color="#ff00ff00">
* <item android:drawable="@android:color/black" />
* </ripple>
*
- * <!-- A blue ripple drawn atop a drawable resource. --/>
+ * <!-- A blue ripple drawn atop a drawable resource. -->
* <ripple android:color="#ff0000ff">
* <item android:drawable="@drawable/my_drawable" />
* </ripple></code>
@@ -108,7 +108,7 @@
* background within the View's hierarchy. In this case, the drawing region
* may extend outside of the Drawable bounds.
* <pre>
- * <code><!-- An unbounded red ripple. --/>
+ * <code><!-- An unbounded red ripple. -->
* <ripple android:color="#ffff0000" /></code>
* </pre>
*
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index df5b3f5..a34d0ab 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -287,7 +287,7 @@
*
* @param computeHyphenation true if you want to use automatic hyphenations.
*/
- public @NonNull Builder setComputeHyphenation(boolean computeHyphenation) {
+ public @NonNull @Deprecated Builder setComputeHyphenation(boolean computeHyphenation) {
setComputeHyphenation(
computeHyphenation ? HYPHENATION_MODE_NORMAL : HYPHENATION_MODE_NONE);
return this;
@@ -331,8 +331,6 @@
*
* {@link #HYPHENATION_MODE_NONE} is by default.
*
- * @see #setComputeHyphenation(boolean)
- *
* @param mode a hyphenation mode.
*/
public @NonNull Builder setComputeHyphenation(@HyphenationMode int mode) {
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 85ef270..df751fc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -27,8 +27,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.view.SurfaceControl;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
@@ -51,9 +49,6 @@
/** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */
private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
- /** Mapping from the client assigned unique token to the TaskFragment {@link SurfaceControl}. */
- private final Map<IBinder, SurfaceControl> mFragmentLeashes = new ArrayMap<>();
-
/**
* Mapping from the client assigned unique token to the TaskFragment parent
* {@link Configuration}.
@@ -67,7 +62,7 @@
* Callback that notifies the controller about changes to task fragments.
*/
interface TaskFragmentCallback {
- void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
@@ -259,15 +254,12 @@
}
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
- final TaskFragmentInfo info = taskFragmentAppearedInfo.getTaskFragmentInfo();
- final IBinder fragmentToken = info.getFragmentToken();
- final SurfaceControl leash = taskFragmentAppearedInfo.getLeash();
- mFragmentInfos.put(fragmentToken, info);
- mFragmentLeashes.put(fragmentToken, leash);
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
+ mFragmentInfos.put(fragmentToken, taskFragmentInfo);
if (mCallback != null) {
- mCallback.onTaskFragmentAppeared(taskFragmentAppearedInfo);
+ mCallback.onTaskFragmentAppeared(taskFragmentInfo);
}
}
@@ -284,7 +276,6 @@
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
- mFragmentLeashes.remove(taskFragmentInfo.getFragmentToken());
mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
if (mCallback != null) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 68c1904..fe6c7ba 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -37,7 +37,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -110,14 +109,13 @@
}
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
- TaskFragmentContainer container = getContainer(
- taskFragmentAppearedInfo.getTaskFragmentInfo().getFragmentToken());
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
if (container == null) {
return;
}
- container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
+ container.setInfo(taskFragmentInfo);
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
}
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
deleted file mode 100644
index e2c67fd..0000000
--- a/libs/WindowManager/Shell/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# sysui owners
-hwwang@google.com
-winsonc@google.com
-madym@google.com
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
similarity index 76%
rename from packages/SystemUI/res/color/prv_text_color_on_accent.xml
rename to libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
index 9f44aca..329e5b9 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
@@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/color/taskbar_background.xml
similarity index 76%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to libs/WindowManager/Shell/res/color/taskbar_background.xml
index 9f44aca..329e5b9 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/color/taskbar_background.xml
@@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
index 94165a1..22cd384 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
@@ -16,6 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="@color/size_compat_hint_bubble"/>
+ <solid android:color="@color/size_compat_background"/>
<corners android:radius="@dimen/size_compat_hint_corner_radius"/>
</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
index a8f0f76..af9063a 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
@@ -20,6 +20,6 @@
android:viewportWidth="10"
android:viewportHeight="8">
<path
- android:fillColor="@color/size_compat_hint_bubble"
+ android:fillColor="@color/size_compat_background"
android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 3e486df..18caa35 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -20,16 +20,16 @@
android:viewportWidth="48"
android:viewportHeight="48">
<path
- android:fillColor="#53534D"
+ android:fillColor="@color/size_compat_background"
android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
<group
android:translateX="12"
android:translateY="12">
<path
- android:fillColor="#E4E3DA"
+ android:fillColor="@color/size_compat_text"
android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
<path
- android:fillColor="#E4E3DA"
+ android:fillColor="@color/size_compat_text"
android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
</group>
</vector>
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
similarity index 74%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
index 9f44aca..95decff 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/size_compat_background_ripple">
+ <item android:drawable="@drawable/size_compat_restart_button"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
index 17347f6..d0e7c42 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
@@ -40,7 +40,7 @@
android:padding="16dp"
android:text="@string/restart_button_description"
android:textAlignment="viewStart"
- android:textColor="#E4E3DA"
+ android:textColor="@color/size_compat_text"
android:textSize="14sp"/>
<ImageView
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
index 47e76f0..82ebee2 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
@@ -30,7 +30,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:src="@drawable/size_compat_restart_button"
+ android:src="@drawable/size_compat_restart_button_ripple"
android:background="@android:color/transparent"
android:contentDescription="@string/restart_button_description"/>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index e488855..6432aef 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Luaskan"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Setelan"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Masuk ke mode layar terpisah"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> adalah picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jika Anda tidak ingin <xliff:g id="NAME">%s</xliff:g> menggunakan fitur ini, ketuk untuk membuka setelan dan menonaktifkannya."</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index afe8584..8b8cb95 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Хаах"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Дэлгэх"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Тохиргоо"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Хуваасан дэлгэцийг оруулна уу"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Цэс"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> дэлгэцэн доторх дэлгэцэд байна"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Та <xliff:g id="NAME">%s</xliff:g>-д энэ онцлогийг ашиглуулахыг хүсэхгүй байвал тохиргоог нээгээд, үүнийг унтраана уу."</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index b495fef..5493ce5 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Kembangkan"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Tetapan"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Masuk skrin pisah"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> terdapat dalam gambar dalam gambar"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Jika anda tidak mahu <xliff:g id="NAME">%s</xliff:g> menggunakan ciri ini, ketik untuk membuka tetapan dan matikan ciri."</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 2849137..e1d17f8 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ပိတ်ရန်"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ချဲ့ရန်"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ဆက်တင်များ"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသို့ ဝင်ရန်"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"မီနူး"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> သည် နှစ်ခုထပ်၍ကြည့်ခြင်း ဖွင့်ထားသည်"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> အား ဤဝန်ဆောင်မှုကို အသုံးမပြုစေလိုလျှင် ဆက်တင်ကိုဖွင့်ရန် တို့ပြီး ၎င်းဝန်ဆောင်မှုကို ပိတ်လိုက်ပါ။"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 2b0b551..7382a48 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Mbyll"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Zgjero"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Cilësimet"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Hyr në ekranin e ndarë"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyja"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> është në figurë brenda figurës"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Nëse nuk dëshiron që <xliff:g id="NAME">%s</xliff:g> ta përdorë këtë funksion, trokit për të hapur cilësimet dhe për ta çaktivizuar."</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 563ec2f..06b04f1 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ปิด"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ขยาย"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"การตั้งค่า"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"เข้าสู่โหมดแบ่งหน้าจอ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"เมนู"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ใช้การแสดงภาพซ้อนภาพ"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"หากคุณไม่ต้องการให้ <xliff:g id="NAME">%s</xliff:g> ใช้ฟีเจอร์นี้ ให้แตะเพื่อเปิดการตั้งค่าแล้วปิดฟีเจอร์"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index a9a36bb..62642c1 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -20,8 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Isara"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Palawakin"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Mga Setting"</string>
- <!-- no translation found for pip_phone_enter_split (7042877263880641911) -->
- <skip />
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Pumasok sa split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Nasa picture-in-picture ang <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Kung ayaw mong magamit ni <xliff:g id="NAME">%s</xliff:g> ang feature na ito, i-tap upang buksan ang mga setting at i-off ito."</string>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index b25a218..23a2172 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -29,7 +29,10 @@
<color name="bubbles_light">#FFFFFF</color>
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
- <color name="size_compat_hint_bubble">#30312B</color>
+
+ <!-- Size Compat Restart Button -->
+ <color name="size_compat_background">@android:color/system_neutral1_800</color>
+ <color name="size_compat_text">@android:color/system_neutral1_50</color>
<!-- GM2 colors -->
<color name="GM2_grey_200">#E8EAED</color>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 75bc461..8e98b82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -29,6 +29,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ActivityInfo;
@@ -122,6 +123,16 @@
}
/**
+ * Callbacks for events in which the focus has changed.
+ */
+ public interface FocusListener {
+ /**
+ * Notifies when the task which is focused has changed.
+ */
+ void onFocusTaskChanged(RunningTaskInfo taskInfo);
+ }
+
+ /**
* Keys map from either a task id or {@link TaskListenerType}.
* @see #addListenerForTaskId
* @see #addListenerForType
@@ -142,6 +153,8 @@
/** @see #addLocusIdListener */
private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>();
+ private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>();
+
private final Object mLock = new Object();
private StartingWindowController mStartingWindow;
@@ -155,6 +168,9 @@
@Nullable
private final Optional<RecentTasksController> mRecentTasks;
+ @Nullable
+ private RunningTaskInfo mLastFocusedTaskInfo;
+
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
Optional.empty() /* recentTasksController */);
@@ -200,6 +216,14 @@
}
}
+ @Override
+ public void unregisterOrganizer() {
+ super.unregisterOrganizer();
+ if (mStartingWindow != null) {
+ mStartingWindow.clearAllWindows();
+ }
+ }
+
public void createRootTask(int displayId, int windowingMode, TaskListener listener) {
ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s",
displayId, windowingMode, listener.toString());
@@ -331,6 +355,27 @@
}
}
+ /**
+ * Adds a listener to be notified for task focus changes.
+ */
+ public void addFocusListener(FocusListener listener) {
+ synchronized (mLock) {
+ mFocusListeners.add(listener);
+ if (mLastFocusedTaskInfo != null) {
+ listener.onFocusTaskChanged(mLastFocusedTaskInfo);
+ }
+ }
+ }
+
+ /**
+ * Removes listener.
+ */
+ public void removeLocusIdListener(FocusListener listener) {
+ synchronized (mLock) {
+ mFocusListeners.remove(listener);
+ }
+ }
+
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
if (mStartingWindow != null) {
@@ -422,6 +467,18 @@
mRecentTasks.ifPresent(recentTasks ->
recentTasks.onTaskWindowingModeChanged(taskInfo));
}
+ // TODO (b/207687679): Remove check for HOME once bug is fixed
+ final boolean isFocusedOrHome = taskInfo.isFocused
+ || (taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME
+ && taskInfo.isVisible);
+ final boolean focusTaskChanged = (mLastFocusedTaskInfo == null
+ || mLastFocusedTaskInfo.taskId != taskInfo.taskId) && isFocusedOrHome;
+ if (focusTaskChanged) {
+ for (int i = 0; i < mFocusListeners.size(); i++) {
+ mFocusListeners.valueAt(i).onFocusTaskChanged(taskInfo);
+ }
+ mLastFocusedTaskInfo = taskInfo;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index c8449a3..c807f66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -291,8 +291,10 @@
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
- pw.println(innerPrefix + "Root taskId=" + getRootTaskId()
- + " winMode=" + mRootTaskInfo.getWindowingMode());
+ if (mRootTaskInfo != null) {
+ pw.println(innerPrefix + "Root taskId=" + mRootTaskInfo.taskId
+ + " winMode=" + mRootTaskInfo.getWindowingMode());
+ }
if (mTaskInfo1 != null) {
pw.println(innerPrefix + "1 taskId=" + mTaskInfo1.taskId
+ " winMode=" + mTaskInfo1.getWindowingMode());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS
new file mode 100644
index 0000000..4d9b520
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-modules apppair owner
+chenghsiuchang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 300319a..b40021e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -32,6 +32,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -529,9 +530,10 @@
// Otherwise, we either tapped the stack (which means we're collapsed
// and should expand) or the currently selected bubble (we're expanded
// and should collapse).
- if (!maybeShowStackEdu()) {
+ if (!maybeShowStackEdu() && !mShowedUserEducationInTouchListenerActive) {
mBubbleData.setExpanded(!mBubbleData.isExpanded());
}
+ mShowedUserEducationInTouchListenerActive = false;
}
}
};
@@ -549,6 +551,14 @@
return true;
}
+ mShowedUserEducationInTouchListenerActive = false;
+ if (maybeShowStackEdu()) {
+ mShowedUserEducationInTouchListenerActive = true;
+ return true;
+ } else if (isStackEduShowing()) {
+ mStackEduView.hide(false /* fromExpansion */);
+ }
+
// If the manage menu is visible, just hide it.
if (mShowingManage) {
showManageMenu(false /* show */);
@@ -607,7 +617,8 @@
// If we're expanding or collapsing, ignore all touch events.
if (mIsExpansionAnimating
// Also ignore events if we shouldn't be draggable.
- || (mPositioner.showingInTaskbar() && !mIsExpanded)) {
+ || (mPositioner.showingInTaskbar() && !mIsExpanded)
+ || mShowedUserEducationInTouchListenerActive) {
return;
}
@@ -628,7 +639,7 @@
mExpandedAnimationController.dragBubbleOut(
v, viewInitialX + dx, viewInitialY + dy);
} else {
- if (mStackEduView != null) {
+ if (isStackEduShowing()) {
mStackEduView.hide(false /* fromExpansion */);
}
mStackAnimationController.moveStackFromTouch(
@@ -646,6 +657,10 @@
|| (mPositioner.showingInTaskbar() && !mIsExpanded)) {
return;
}
+ if (mShowedUserEducationInTouchListenerActive) {
+ mShowedUserEducationInTouchListenerActive = false;
+ return;
+ }
// First, see if the magnetized object consumes the event - if so, the bubble was
// released in the target or flung out of it, and we should ignore the event.
@@ -738,6 +753,7 @@
private ImageView mManageSettingsIcon;
private TextView mManageSettingsText;
private boolean mShowingManage = false;
+ private boolean mShowedUserEducationInTouchListenerActive = false;
private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
private BubblePositioner mPositioner;
@@ -929,10 +945,12 @@
showManageMenu(false /* show */);
} else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
mManageEduView.hide();
- } else if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ } else if (isStackEduShowing()) {
mStackEduView.hide(false /* isExpanding */);
} else if (mBubbleData.isExpanded()) {
mBubbleData.setExpanded(false);
+ } else {
+ maybeShowStackEdu();
}
});
@@ -1116,6 +1134,9 @@
* Whether the educational view should show for the expanded view "manage" menu.
*/
private boolean shouldShowManageEdu() {
+ if (ActivityManager.isRunningInTestHarness()) {
+ return false;
+ }
final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
&& mExpandedBubble != null;
@@ -1140,6 +1161,9 @@
* Whether education view should show for the collapsed stack.
*/
private boolean shouldShowStackEdu() {
+ if (ActivityManager.isRunningInTestHarness()) {
+ return false;
+ }
final boolean seen = getPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION);
final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
@@ -1157,7 +1181,7 @@
* @return true if education view for collapsed stack should show and was not showing before.
*/
private boolean maybeShowStackEdu() {
- if (!shouldShowStackEdu()) {
+ if (!shouldShowStackEdu() || isExpanded()) {
return false;
}
if (mStackEduView == null) {
@@ -1168,9 +1192,13 @@
return mStackEduView.show(mPositioner.getDefaultStartPosition());
}
+ private boolean isStackEduShowing() {
+ return mStackEduView != null && mStackEduView.getVisibility() == VISIBLE;
+ }
+
// Recreates & shows the education views. Call when a theme/config change happens.
private void updateUserEdu() {
- if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ if (isStackEduShowing()) {
removeView(mStackEduView);
mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
addView(mStackEduView);
@@ -1852,7 +1880,7 @@
cancelDelayedExpandCollapseSwitchAnimations();
final boolean showVertically = mPositioner.showBubblesVertically();
mIsExpanded = true;
- if (mStackEduView != null) {
+ if (isStackEduShowing()) {
mStackEduView.hide(true /* fromExpansion */);
}
beforeExpandedViewAnimation();
@@ -2390,7 +2418,7 @@
if (flyoutMessage == null
|| flyoutMessage.message == null
|| !bubble.showFlyout()
- || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE)
+ || isStackEduShowing()
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
@@ -2512,7 +2540,7 @@
* them.
*/
public void getTouchableRegion(Rect outRect) {
- if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ if (isStackEduShowing()) {
// When user education shows then capture all touches
outRect.set(0, 0, getWidth(), getHeight());
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS
new file mode 100644
index 0000000..8271014
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module bubble owner
+madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index f6a90b7..3846de7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -125,6 +125,7 @@
* @return true if user education was shown, false otherwise.
*/
fun show(stackPosition: PointF): Boolean {
+ isHiding = false
if (visibility == VISIBLE) return false
controller.updateWindowFlagsForBackpress(true /* interceptBack */)
@@ -164,6 +165,7 @@
*/
fun hide(isExpanding: Boolean) {
if (visibility != VISIBLE || isHiding) return
+ isHiding = true
controller.updateWindowFlagsForBackpress(false /* interceptBack */)
animate()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index d07fff3..3579bf4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -321,6 +321,16 @@
mSplitLayoutHandler.onLayoutSizeChanged(this);
}
+ /** Sets divide position base on the ratio within root bounds. */
+ public void setDivideRatio(float ratio) {
+ final int position = isLandscape()
+ ? mRootBounds.left + (int) (mRootBounds.width() * ratio)
+ : mRootBounds.top + (int) (mRootBounds.height() * ratio);
+ DividerSnapAlgorithm.SnapTarget snapTarget =
+ mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+ setDividePosition(snapTarget.position);
+ }
+
/** Resets divider position. */
public void resetDividerPosition() {
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
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 d239e56..54ce6bb 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
@@ -56,6 +56,7 @@
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
@@ -156,8 +157,16 @@
@WMSingleton
@Provides
static DragAndDropController provideDragAndDropController(Context context,
- DisplayController displayController, UiEventLogger uiEventLogger) {
- return new DragAndDropController(context, displayController, uiEventLogger);
+ DisplayController displayController, UiEventLogger uiEventLogger,
+ IconProvider iconProvider, @ShellMainThread ShellExecutor mainExecutor) {
+ return new DragAndDropController(context, displayController, uiEventLogger, iconProvider,
+ mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static DragAndDrop provideDragAndDrop(DragAndDropController dragAndDropController) {
+ return dragAndDropController.asDragAndDrop();
}
@WMSingleton
@@ -244,15 +253,24 @@
// Fullscreen
//
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract FullscreenTaskListener optionalFullscreenTaskListener();
+
@WMSingleton
@Provides
static FullscreenTaskListener provideFullscreenTaskListener(
+ @DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener,
SyncTransactionQueue syncQueue,
Optional<FullscreenUnfoldController> optionalFullscreenUnfoldController,
- Optional<RecentTasksController> recentTasksOptional
- ) {
- return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
- recentTasksOptional);
+ Optional<RecentTasksController> recentTasksOptional) {
+ if (fullscreenTaskListener.isPresent()) {
+ return fullscreenTaskListener.get();
+ } else {
+ return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
+ recentTasksOptional);
+ }
}
//
@@ -337,7 +355,7 @@
@Provides
static Optional<OneHandedController> providesOneHandedController(
@DynamicOverride Optional<OneHandedController> oneHandedController) {
- if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ if (SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
return oneHandedController;
}
return Optional.empty();
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 46c7b50..f562fd9c 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
@@ -241,10 +241,11 @@
static PhonePipMenuController providesPipPhoneMenuController(Context context,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
SystemWindows systemWindows,
+ Optional<SplitScreenController> splitScreenOptional,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
- systemWindows, mainExecutor, mainHandler);
+ systemWindows, splitScreenOptional, mainExecutor, mainHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java
new file mode 100644
index 0000000..edeff6e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java
@@ -0,0 +1,34 @@
+/*
+ * 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.draganddrop;
+
+import android.content.res.Configuration;
+
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface for telling DragAndDrop stuff.
+ */
+@ExternalThread
+public interface DragAndDrop {
+
+ /** Called when the theme changes. */
+ void onThemeChanged();
+
+ /** Called when the configuration changes. */
+ void onConfigChanged(Configuration newConfig);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index d2b4711..101295d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -41,7 +41,6 @@
import android.graphics.PixelFormat;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.Display;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
@@ -53,8 +52,10 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -71,16 +72,26 @@
private final Context mContext;
private final DisplayController mDisplayController;
private final DragAndDropEventLogger mLogger;
+ private final IconProvider mIconProvider;
private SplitScreenController mSplitScreen;
+ private ShellExecutor mMainExecutor;
+ private DragAndDropImpl mImpl;
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
public DragAndDropController(Context context, DisplayController displayController,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger, IconProvider iconProvider, ShellExecutor mainExecutor) {
mContext = context;
mDisplayController = displayController;
mLogger = new DragAndDropEventLogger(uiEventLogger);
+ mIconProvider = iconProvider;
+ mMainExecutor = mainExecutor;
+ mImpl = new DragAndDropImpl();
+ }
+
+ public DragAndDrop asDragAndDrop() {
+ return mImpl;
}
public void initialize(Optional<SplitScreenController> splitscreen) {
@@ -117,7 +128,7 @@
R.layout.global_drop_target, null);
rootView.setOnDragListener(this);
rootView.setVisibility(View.INVISIBLE);
- DragLayout dragLayout = new DragLayout(context, mSplitScreen);
+ DragLayout dragLayout = new DragLayout(context, mSplitScreen, mIconProvider);
rootView.addView(dragLayout,
new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
try {
@@ -267,6 +278,18 @@
return mimeTypes;
}
+ private void onThemeChange() {
+ for (int i = 0; i < mDisplayDropTargets.size(); i++) {
+ mDisplayDropTargets.get(i).dragLayout.onThemeChange();
+ }
+ }
+
+ private void onConfigChanged(Configuration newConfig) {
+ for (int i = 0; i < mDisplayDropTargets.size(); i++) {
+ mDisplayDropTargets.get(i).dragLayout.onConfigChanged(newConfig);
+ }
+ }
+
private static class PerDisplay {
final int displayId;
final Context context;
@@ -287,4 +310,21 @@
dragLayout = dl;
}
}
+
+ private class DragAndDropImpl implements DragAndDrop {
+
+ @Override
+ public void onThemeChanged() {
+ mMainExecutor.execute(() -> {
+ DragAndDropController.this.onThemeChange();
+ });
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mMainExecutor.execute(() -> {
+ DragAndDropController.this.onConfigChanged(newConfig);
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index fbf04d6..d2bd28e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -37,8 +37,6 @@
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -66,7 +64,6 @@
import com.android.internal.logging.InstanceId;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
-import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
@@ -198,29 +195,23 @@
return;
}
- final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
- @StageType int stage = STAGE_TYPE_UNDEFINED;
@SplitPosition int position = SPLIT_POSITION_UNDEFINED;
if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
// Update launch options for the split side we are targeting.
position = leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
- if (!inSplitScreen) {
- // Launch in the side stage if we are not in split-screen already.
- stage = STAGE_TYPE_SIDE;
- }
// Add some data for logging splitscreen once it is invoked
mSplitScreen.logOnDroppedToSplit(position, mLoggerSessionId);
}
final ClipDescription description = data.getDescription();
final Intent dragData = mSession.dragData;
- startClipDescription(description, dragData, stage, position);
+ startClipDescription(description, dragData, position);
}
private void startClipDescription(ClipDescription description, Intent intent,
- @StageType int stage, @SplitPosition int position) {
+ @SplitPosition int position) {
final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
@@ -228,15 +219,15 @@
if (isTask) {
final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
- mStarter.startTask(taskId, stage, position, opts);
+ mStarter.startTask(taskId, position, opts);
} else if (isShortcut) {
final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
- mStarter.startShortcut(packageName, id, stage, position, opts, user);
+ mStarter.startShortcut(packageName, id, position, opts, user);
} else {
mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT),
- null, stage, position, opts);
+ null, position, opts);
}
}
@@ -291,12 +282,10 @@
* Interface for actually committing the task launches.
*/
public interface Starter {
- void startTask(int taskId, @StageType int stage, @SplitPosition int position,
- @Nullable Bundle options);
- void startShortcut(String packageName, String shortcutId, @StageType int stage,
- @SplitPosition int position, @Nullable Bundle options, UserHandle user);
- void startIntent(PendingIntent intent, Intent fillInIntent,
- @StageType int stage, @SplitPosition int position,
+ void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
+ @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
@@ -319,8 +308,7 @@
}
@Override
- public void startTask(int taskId, int stage, int position,
- @Nullable Bundle options) {
+ public void startTask(int taskId, int position, @Nullable Bundle options) {
try {
ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
} catch (RemoteException e) {
@@ -329,7 +317,7 @@
}
@Override
- public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ public void startShortcut(String packageName, String shortcutId, int position,
@Nullable Bundle options, UserHandle user) {
try {
LauncherApps launcherApps =
@@ -342,8 +330,8 @@
}
@Override
- public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int stage,
- int position, @Nullable Bundle options) {
+ public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int position,
+ @Nullable Bundle options) {
try {
intent.send(mContext, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index efc9ed0..20d8054 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -16,78 +16,138 @@
package com.android.wm.shell.draganddrop;
-import static com.android.wm.shell.animation.Interpolators.FAST_OUT_LINEAR_IN;
-import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.wm.shell.animation.Interpolators.LINEAR;
-import static com.android.wm.shell.animation.Interpolators.LINEAR_OUT_SLOW_IN;
+import static android.app.StatusBarManager.DISABLE_NONE;
+
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.StatusBarManager;
import android.content.ClipData;
import android.content.Context;
-import android.graphics.Canvas;
+import android.content.res.Configuration;
+import android.graphics.Color;
import android.graphics.Insets;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
import android.view.DragEvent;
import android.view.SurfaceControl;
-import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
-
-import androidx.annotation.NonNull;
+import android.widget.LinearLayout;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.ArrayList;
+import java.util.List;
/**
* Coordinates the visible drop targets for the current drag.
*/
-public class DragLayout extends View {
+public class DragLayout extends LinearLayout {
+
+ // While dragging the status bar is hidden.
+ private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS
+ | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+ | StatusBarManager.DISABLE_CLOCK
+ | StatusBarManager.DISABLE_SYSTEM_INFO;
private final DragAndDropPolicy mPolicy;
+ private final SplitScreenController mSplitScreenController;
+ private final IconProvider mIconProvider;
+ private final StatusBarManager mStatusBarManager;
private DragAndDropPolicy.Target mCurrentTarget = null;
- private DropOutlineDrawable mDropOutline;
+ private DropZoneView mDropZoneView1;
+ private DropZoneView mDropZoneView2;
+
private int mDisplayMargin;
private Insets mInsets = Insets.NONE;
private boolean mIsShowing;
private boolean mHasDropped;
- public DragLayout(Context context, SplitScreenController splitscreen) {
+ @SuppressLint("WrongConstant")
+ public DragLayout(Context context, SplitScreenController splitScreenController,
+ IconProvider iconProvider) {
super(context);
- mPolicy = new DragAndDropPolicy(context, splitscreen);
+ mSplitScreenController = splitScreenController;
+ mIconProvider = iconProvider;
+ mPolicy = new DragAndDropPolicy(context, splitScreenController);
+ mStatusBarManager = context.getSystemService(StatusBarManager.class);
+
mDisplayMargin = context.getResources().getDimensionPixelSize(
R.dimen.drop_layout_display_margin);
- mDropOutline = new DropOutlineDrawable(context);
- setBackground(mDropOutline);
- setWillNotDraw(false);
+
+ mDropZoneView1 = new DropZoneView(context);
+ mDropZoneView2 = new DropZoneView(context);
+ addView(mDropZoneView1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ addView(mDropZoneView2, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ ((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1;
+ ((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1;
+ updateContainerMargins();
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mInsets = insets.getInsets(Type.systemBars() | Type.displayCutout());
recomputeDropTargets();
+
+ final int orientation = getResources().getConfiguration().orientation;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ mDropZoneView1.setBottomInset(mInsets.bottom);
+ mDropZoneView2.setBottomInset(mInsets.bottom);
+ } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ mDropZoneView1.setBottomInset(0);
+ mDropZoneView2.setBottomInset(mInsets.bottom);
+ }
return super.onApplyWindowInsets(insets);
}
- @Override
- protected boolean verifyDrawable(@NonNull Drawable who) {
- return who == mDropOutline || super.verifyDrawable(who);
+ public void onThemeChange() {
+ mDropZoneView1.onThemeChange();
+ mDropZoneView2.onThemeChange();
}
- @Override
- protected void onDraw(Canvas canvas) {
- if (mCurrentTarget != null) {
- mDropOutline.draw(canvas);
+ public void onConfigChanged(Configuration newConfig) {
+ final int orientation = getResources().getConfiguration().orientation;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE
+ && getOrientation() != HORIZONTAL) {
+ setOrientation(LinearLayout.HORIZONTAL);
+ updateContainerMargins();
+ } else if (orientation == Configuration.ORIENTATION_PORTRAIT
+ && getOrientation() != VERTICAL) {
+ setOrientation(LinearLayout.VERTICAL);
+ updateContainerMargins();
+ }
+ }
+
+ private void updateContainerMargins() {
+ final int orientation = getResources().getConfiguration().orientation;
+ final float halfMargin = mDisplayMargin / 2f;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ mDropZoneView1.setContainerMargin(
+ mDisplayMargin, mDisplayMargin, halfMargin, mDisplayMargin);
+ mDropZoneView2.setContainerMargin(
+ halfMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin);
+ } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ mDropZoneView1.setContainerMargin(
+ mDisplayMargin, mDisplayMargin, mDisplayMargin, halfMargin);
+ mDropZoneView2.setContainerMargin(
+ mDisplayMargin, halfMargin, mDisplayMargin, mDisplayMargin);
}
}
@@ -104,6 +164,43 @@
mPolicy.start(displayLayout, initialData, loggerSessionId);
mHasDropped = false;
mCurrentTarget = null;
+
+ List<ActivityManager.RunningTaskInfo> tasks = null;
+ // Figure out the splashscreen info for the existing task(s).
+ try {
+ tasks = ActivityTaskManager.getService().getTasks(2,
+ false /* filterOnlyVisibleRecents */,
+ false /* keepIntentExtra */);
+ } catch (RemoteException e) {
+ // don't show an icon / will just use the defaults
+ }
+ if (tasks != null && !tasks.isEmpty()) {
+ ActivityManager.RunningTaskInfo taskInfo1 = tasks.get(0);
+ Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
+ int bgColor1 = getResizingBackgroundColor(taskInfo1);
+
+ boolean alreadyInSplit = mSplitScreenController != null
+ && mSplitScreenController.isSplitScreenVisible();
+ if (alreadyInSplit && tasks.size() > 1) {
+ ActivityManager.RunningTaskInfo taskInfo2 = tasks.get(1);
+ Drawable icon2 = mIconProvider.getIcon(taskInfo2.topActivityInfo);
+ int bgColor2 = getResizingBackgroundColor(taskInfo2);
+
+ // figure out which task is on which side
+ int splitPosition1 = mSplitScreenController.getSplitPosition(taskInfo1.taskId);
+ boolean isTask1TopOrLeft = splitPosition1 == SPLIT_POSITION_TOP_OR_LEFT;
+ if (isTask1TopOrLeft) {
+ mDropZoneView1.setAppInfo(bgColor1, icon1);
+ mDropZoneView2.setAppInfo(bgColor2, icon2);
+ } else {
+ mDropZoneView2.setAppInfo(bgColor1, icon1);
+ mDropZoneView1.setAppInfo(bgColor2, icon2);
+ }
+ } else {
+ mDropZoneView1.setAppInfo(bgColor1, icon1);
+ mDropZoneView2.setAppInfo(bgColor1, icon1);
+ }
+ }
}
public void show() {
@@ -139,20 +236,14 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Current target: %s", target);
if (target == null) {
// Animating to no target
- mDropOutline.startVisibilityAnimation(false, LINEAR);
- Rect finalBounds = new Rect(mCurrentTarget.drawRegion);
- finalBounds.inset(mDisplayMargin, mDisplayMargin);
- mDropOutline.startBoundsAnimation(finalBounds, FAST_OUT_LINEAR_IN);
+ animateSplitContainers(false, null /* animCompleteCallback */);
} else if (mCurrentTarget == null) {
// Animating to first target
- mDropOutline.startVisibilityAnimation(true, LINEAR);
- Rect initialBounds = new Rect(target.drawRegion);
- initialBounds.inset(mDisplayMargin, mDisplayMargin);
- mDropOutline.setRegionBounds(initialBounds);
- mDropOutline.startBoundsAnimation(target.drawRegion, LINEAR_OUT_SLOW_IN);
+ animateSplitContainers(true, null /* animCompleteCallback */);
+ animateHighlight(target);
} else {
- // Bounds change
- mDropOutline.startBoundsAnimation(target.drawRegion, FAST_OUT_SLOW_IN);
+ // Switching between targets
+ animateHighlight(target);
}
mCurrentTarget = target;
}
@@ -163,26 +254,7 @@
*/
public void hide(DragEvent event, Runnable hideCompleteCallback) {
mIsShowing = false;
- ObjectAnimator alphaAnimator = mDropOutline.startVisibilityAnimation(false, LINEAR);
- ObjectAnimator boundsAnimator = null;
- if (mCurrentTarget != null) {
- Rect finalBounds = new Rect(mCurrentTarget.drawRegion);
- finalBounds.inset(mDisplayMargin, mDisplayMargin);
- boundsAnimator = mDropOutline.startBoundsAnimation(finalBounds, FAST_OUT_LINEAR_IN);
- }
-
- if (hideCompleteCallback != null) {
- ObjectAnimator lastAnim = boundsAnimator != null
- ? boundsAnimator
- : alphaAnimator;
- lastAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- hideCompleteCallback.run();
- }
- });
- }
-
+ animateSplitContainers(false, hideCompleteCallback);
mCurrentTarget = null;
}
@@ -201,4 +273,49 @@
hide(event, dropCompleteCallback);
return handledDrop;
}
+
+ private void animateSplitContainers(boolean visible, Runnable animCompleteCallback) {
+ mStatusBarManager.disable(visible
+ ? HIDE_STATUS_BAR_FLAGS
+ : DISABLE_NONE);
+ mDropZoneView1.setShowingMargin(visible);
+ mDropZoneView2.setShowingMargin(visible);
+ ObjectAnimator animator = mDropZoneView1.getAnimator();
+ if (animCompleteCallback != null) {
+ if (animator != null) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animCompleteCallback.run();
+ }
+ });
+ } else {
+ // If there's no animator the animation is done so run immediately
+ animCompleteCallback.run();
+ }
+ }
+ }
+
+ private void animateHighlight(DragAndDropPolicy.Target target) {
+ if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_LEFT
+ || target.type == DragAndDropPolicy.Target.TYPE_SPLIT_TOP) {
+ mDropZoneView1.setShowingHighlight(true);
+ mDropZoneView1.setShowingSplash(false);
+
+ mDropZoneView2.setShowingHighlight(false);
+ mDropZoneView2.setShowingSplash(true);
+ } else if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT
+ || target.type == DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM) {
+ mDropZoneView1.setShowingHighlight(false);
+ mDropZoneView1.setShowingSplash(true);
+
+ mDropZoneView2.setShowingHighlight(true);
+ mDropZoneView2.setShowingSplash(false);
+ }
+ }
+
+ private static int getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
new file mode 100644
index 0000000..2f47af5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -0,0 +1,313 @@
+/*
+ * 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.wm.shell.draganddrop;
+
+import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Path;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.IntProperty;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.wm.shell.R;
+
+/**
+ * Renders a drop zone area for items being dragged.
+ */
+public class DropZoneView extends FrameLayout {
+
+ private static final int SPLASHSCREEN_ALPHA_INT = (int) (255 * 0.90f);
+ private static final int HIGHLIGHT_ALPHA_INT = 255;
+ private static final int MARGIN_ANIMATION_ENTER_DURATION = 400;
+ private static final int MARGIN_ANIMATION_EXIT_DURATION = 250;
+
+ private static final FloatProperty<DropZoneView> INSETS =
+ new FloatProperty<DropZoneView>("insets") {
+ @Override
+ public void setValue(DropZoneView v, float percent) {
+ v.setMarginPercent(percent);
+ }
+
+ @Override
+ public Float get(DropZoneView v) {
+ return v.getMarginPercent();
+ }
+ };
+
+ private static final IntProperty<ColorDrawable> SPLASHSCREEN_ALPHA =
+ new IntProperty<ColorDrawable>("splashscreen") {
+ @Override
+ public void setValue(ColorDrawable d, int alpha) {
+ d.setAlpha(alpha);
+ }
+
+ @Override
+ public Integer get(ColorDrawable d) {
+ return d.getAlpha();
+ }
+ };
+
+ private static final IntProperty<ColorDrawable> HIGHLIGHT_ALPHA =
+ new IntProperty<ColorDrawable>("highlight") {
+ @Override
+ public void setValue(ColorDrawable d, int alpha) {
+ d.setAlpha(alpha);
+ }
+
+ @Override
+ public Integer get(ColorDrawable d) {
+ return d.getAlpha();
+ }
+ };
+
+ private final Path mPath = new Path();
+ private final float[] mContainerMargin = new float[4];
+ private float mCornerRadius;
+ private float mBottomInset;
+ private int mMarginColor; // i.e. color used for negative space like the container insets
+ private int mHighlightColor;
+
+ private boolean mShowingHighlight;
+ private boolean mShowingSplash;
+ private boolean mShowingMargin;
+
+ // TODO: might be more seamless to animate between splash/highlight color instead of 2 separate
+ private ObjectAnimator mSplashAnimator;
+ private ObjectAnimator mHighlightAnimator;
+ private ObjectAnimator mMarginAnimator;
+ private float mMarginPercent;
+
+ // Renders a highlight or neutral transparent color
+ private ColorDrawable mDropZoneDrawable;
+ // Renders the translucent splashscreen with the app icon in the middle
+ private ImageView mSplashScreenView;
+ private ColorDrawable mSplashBackgroundDrawable;
+ // Renders the margin / insets around the dropzone container
+ private MarginView mMarginView;
+
+ public DropZoneView(Context context) {
+ this(context, null);
+ }
+
+ public DropZoneView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DropZoneView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DropZoneView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setContainerMargin(0, 0, 0, 0); // make sure it's populated
+
+ mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mMarginColor = getResources().getColor(R.color.taskbar_background);
+ mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
+
+ mDropZoneDrawable = new ColorDrawable();
+ mDropZoneDrawable.setColor(mHighlightColor);
+ mDropZoneDrawable.setAlpha(0);
+ setBackgroundDrawable(mDropZoneDrawable);
+
+ mSplashScreenView = new ImageView(context);
+ mSplashScreenView.setScaleType(ImageView.ScaleType.CENTER);
+ mSplashBackgroundDrawable = new ColorDrawable();
+ mSplashBackgroundDrawable.setColor(Color.WHITE);
+ mSplashBackgroundDrawable.setAlpha(SPLASHSCREEN_ALPHA_INT);
+ mSplashScreenView.setBackgroundDrawable(mSplashBackgroundDrawable);
+ addView(mSplashScreenView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mSplashScreenView.setAlpha(0f);
+
+ mMarginView = new MarginView(context);
+ addView(mMarginView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ }
+
+ public void onThemeChange() {
+ mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(getContext());
+ mMarginColor = getResources().getColor(R.color.taskbar_background);
+ mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
+
+ final int alpha = mDropZoneDrawable.getAlpha();
+ mDropZoneDrawable.setColor(mHighlightColor);
+ mDropZoneDrawable.setAlpha(alpha);
+
+ if (mMarginPercent > 0) {
+ mMarginView.invalidate();
+ }
+ }
+
+ /** Sets the desired margins around the drop zone container when fully showing. */
+ public void setContainerMargin(float left, float top, float right, float bottom) {
+ mContainerMargin[0] = left;
+ mContainerMargin[1] = top;
+ mContainerMargin[2] = right;
+ mContainerMargin[3] = bottom;
+ if (mMarginPercent > 0) {
+ mMarginView.invalidate();
+ }
+ }
+
+ /** Sets the bottom inset so the drop zones are above bottom navigation. */
+ public void setBottomInset(float bottom) {
+ mBottomInset = bottom;
+ ((LayoutParams) mSplashScreenView.getLayoutParams()).bottomMargin = (int) bottom;
+ if (mMarginPercent > 0) {
+ mMarginView.invalidate();
+ }
+ }
+
+ /** Sets the color and icon to use for the splashscreen when shown. */
+ public void setAppInfo(int splashScreenColor, Drawable appIcon) {
+ mSplashBackgroundDrawable.setColor(splashScreenColor);
+ mSplashScreenView.setImageDrawable(appIcon);
+ }
+
+ /** @return an active animator for this view if one exists. */
+ @Nullable
+ public ObjectAnimator getAnimator() {
+ if (mMarginAnimator != null && mMarginAnimator.isRunning()) {
+ return mMarginAnimator;
+ } else if (mHighlightAnimator != null && mHighlightAnimator.isRunning()) {
+ return mHighlightAnimator;
+ } else if (mSplashAnimator != null && mSplashAnimator.isRunning()) {
+ return mSplashAnimator;
+ }
+ return null;
+ }
+
+ /** Animates the splashscreen to show or hide. */
+ public void setShowingSplash(boolean showingSplash) {
+ if (mShowingSplash != showingSplash) {
+ mShowingSplash = showingSplash;
+ animateSplashToState();
+ }
+ }
+
+ /** Animates the highlight indicating the zone is hovered on or not. */
+ public void setShowingHighlight(boolean showingHighlight) {
+ if (mShowingHighlight != showingHighlight) {
+ mShowingHighlight = showingHighlight;
+ animateHighlightToState();
+ }
+ }
+
+ /** Animates the margins around the drop zone to show or hide. */
+ public void setShowingMargin(boolean visible) {
+ if (mShowingMargin != visible) {
+ mShowingMargin = visible;
+ animateMarginToState();
+ }
+ if (!mShowingMargin) {
+ setShowingHighlight(false);
+ setShowingSplash(false);
+ }
+ }
+
+ private void animateSplashToState() {
+ if (mSplashAnimator != null) {
+ mSplashAnimator.cancel();
+ }
+ mSplashAnimator = ObjectAnimator.ofInt(mSplashBackgroundDrawable,
+ SPLASHSCREEN_ALPHA,
+ mSplashBackgroundDrawable.getAlpha(),
+ mShowingSplash ? SPLASHSCREEN_ALPHA_INT : 0);
+ if (!mShowingSplash) {
+ mSplashAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ }
+ mSplashAnimator.start();
+ mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start();
+ }
+
+ private void animateHighlightToState() {
+ if (mHighlightAnimator != null) {
+ mHighlightAnimator.cancel();
+ }
+ mHighlightAnimator = ObjectAnimator.ofInt(mDropZoneDrawable,
+ HIGHLIGHT_ALPHA,
+ mDropZoneDrawable.getAlpha(),
+ mShowingHighlight ? HIGHLIGHT_ALPHA_INT : 0);
+ if (!mShowingHighlight) {
+ mHighlightAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ }
+ mHighlightAnimator.start();
+ }
+
+ private void animateMarginToState() {
+ if (mMarginAnimator != null) {
+ mMarginAnimator.cancel();
+ }
+ mMarginAnimator = ObjectAnimator.ofFloat(this, INSETS,
+ mMarginPercent,
+ mShowingMargin ? 1f : 0f);
+ mMarginAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ mMarginAnimator.setDuration(mShowingMargin
+ ? MARGIN_ANIMATION_ENTER_DURATION
+ : MARGIN_ANIMATION_EXIT_DURATION);
+ mMarginAnimator.start();
+ }
+
+ private void setMarginPercent(float percent) {
+ if (percent != mMarginPercent) {
+ mMarginPercent = percent;
+ mMarginView.invalidate();
+ }
+ }
+
+ private float getMarginPercent() {
+ return mMarginPercent;
+ }
+
+ /** Simple view that draws a rounded rect margin around its contents. **/
+ private class MarginView extends View {
+
+ MarginView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ mPath.reset();
+ mPath.addRoundRect(mContainerMargin[0] * mMarginPercent,
+ mContainerMargin[1] * mMarginPercent,
+ getWidth() - (mContainerMargin[2] * mMarginPercent),
+ getHeight() - (mContainerMargin[3] * mMarginPercent) - mBottomInset,
+ mCornerRadius * mMarginPercent,
+ mCornerRadius * mMarginPercent,
+ Path.Direction.CW);
+ mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
+ canvas.clipPath(mPath);
+ canvas.drawColor(mMarginColor);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 5c8e7d0..52ff21b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.freeform;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
@@ -28,7 +27,6 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.SurfaceControl;
-import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -85,13 +83,6 @@
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
return;
}
-
- // Clears windowing mode and window bounds to let the task inherits from its new parent.
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(taskInfo.token, null)
- .setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
- mSyncQueue.queue(wct);
-
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OWNERS
new file mode 100644
index 0000000..41177f0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module one handed mode owner
+lbill@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
new file mode 100644
index 0000000..afddfab
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module pip owner
+hwwang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index 8d9ad4d..caa1f01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -90,6 +90,11 @@
default void updateMenuBounds(Rect destinationBounds) {}
/**
+ * Update when the current focused task changes.
+ */
+ default void onFocusTaskChanged(RunningTaskInfo taskInfo) {}
+
+ /**
* Returns a default LayoutParams for the PIP Menu.
* @param width the PIP stack width.
* @param height the PIP stack height.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 6cc5f09..854fc60e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -98,7 +98,7 @@
* see also {@link PipMotionHelper}.
*/
public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
- DisplayController.OnDisplaysChangedListener {
+ DisplayController.OnDisplaysChangedListener, ShellTaskOrganizer.FocusListener {
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
private static final boolean DEBUG = false;
/**
@@ -286,6 +286,7 @@
mMainExecutor.execute(() -> {
mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
});
+ mTaskOrganizer.addFocusListener(this);
mPipTransitionController.setPipOrganizer(this);
displayController.addDisplayWindowListener(this);
}
@@ -772,6 +773,11 @@
}
@Override
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mPipMenuController.onFocusTaskChanged(taskInfo);
+ }
+
+ @Override
public boolean supportSizeCompatUI() {
// PIP doesn't support size compat.
return false;
@@ -1249,11 +1255,7 @@
} else if (isOutPipDirection(direction)) {
// If we are animating to fullscreen or split screen, then we need to reset the
// override bounds on the task to ensure that the task "matches" the parent's bounds.
- if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
- taskBounds = destinationBounds;
- } else {
- taskBounds = null;
- }
+ taskBounds = null;
applyWindowingModeChangeOnExit(wct, direction);
} else {
// Just a resize in PIP
@@ -1282,6 +1284,9 @@
}
private boolean isPipTopLeft() {
+ if (!mSplitScreenOptional.isPresent()) {
+ return false;
+ }
final Rect topLeft = new Rect();
final Rect bottomRight = new Rect();
mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 5687f4d..eb512af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.RemoteAction;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -43,10 +44,12 @@
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Manages the PiP menu view which can show menu options or a scrim.
@@ -114,6 +117,7 @@
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final SystemWindows mSystemWindows;
+ private final Optional<SplitScreenController> mSplitScreenController;
private ParceledListSlice<RemoteAction> mAppActions;
private ParceledListSlice<RemoteAction> mMediaActions;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -145,6 +149,7 @@
public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
PipMediaController mediaController, SystemWindows systemWindows,
+ Optional<SplitScreenController> splitScreenOptional,
ShellExecutor mainExecutor, Handler mainHandler) {
mContext = context;
mPipBoundsState = pipBoundsState;
@@ -152,6 +157,7 @@
mSystemWindows = systemWindows;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mSplitScreenController = splitScreenOptional;
}
public boolean isMenuVisible() {
@@ -180,7 +186,8 @@
if (mPipMenuView != null) {
detachPipMenuView();
}
- mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler);
+ mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
+ mSplitScreenController);
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
@@ -209,6 +216,13 @@
updateMenuLayout(destinationBounds);
}
+ @Override
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mPipMenuView != null) {
+ mPipMenuView.onFocusTaskChanged(taskInfo);
+ }
+ }
+
/**
* Tries to grab a surface control from {@link PipMenuView}. If this isn't available for some
* reason (ie. the window isn't ready yet, thus {@link android.view.ViewRootImpl} is
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index b209699..82e8273 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip.phone;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
@@ -32,8 +33,10 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.app.ActivityManager;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -61,11 +64,13 @@
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Translucent window that gets started on top of a task in PIP to allow the user to control it.
@@ -105,6 +110,7 @@
private boolean mAllowMenuTimeout = true;
private boolean mAllowTouches = true;
private int mDismissFadeOutDurationMs;
+ private boolean mFocusedTaskAllowSplitScreen;
private final List<RemoteAction> mActions = new ArrayList<>();
@@ -116,6 +122,7 @@
private AnimatorSet mMenuContainerAnimator;
private PhonePipMenuController mController;
+ private Optional<SplitScreenController> mSplitScreenControllerOptional;
private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -144,12 +151,14 @@
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
public PipMenuView(Context context, PhonePipMenuController controller,
- ShellExecutor mainExecutor, Handler mainHandler) {
+ ShellExecutor mainExecutor, Handler mainHandler,
+ Optional<SplitScreenController> splitScreenController) {
super(context, null, 0);
mContext = context;
mController = controller;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mSplitScreenControllerOptional = splitScreenController;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
@@ -255,6 +264,15 @@
return super.dispatchGenericMotionEvent(event);
}
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ final boolean isSplitScreen = mSplitScreenControllerOptional.isPresent()
+ && mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId);
+ mFocusedTaskAllowSplitScreen = isSplitScreen
+ || (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && taskInfo.supportsSplitScreenMultiWindow
+ && taskInfo.topActivityType != WindowConfiguration.ACTIVITY_TYPE_HOME);
+ }
+
void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
mAllowMenuTimeout = allowMenuTimeout;
@@ -278,7 +296,8 @@
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f);
ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
- mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
+ mEnterSplitButton.getAlpha(),
+ ENABLE_ENTER_SPLIT && mFocusedTaskAllowSplitScreen ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
enterSplitAnim);
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 00083d9..83390a5 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
@@ -57,7 +57,7 @@
public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipMenuController.Delegate, TvPipNotificationController.Delegate {
private static final String TAG = "TvPipController";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
private static final int NONEXISTENT_TASK_ID = -1;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 7cf3baf..a006f30 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -111,6 +111,11 @@
if (taskId1 == taskId2) {
return;
}
+ if (mSplitTasks.get(taskId1, INVALID_TASK_ID) == taskId2
+ && mTaskSplitBoundsMap.get(taskId1).equals(splitBounds)) {
+ // If the two tasks are already paired and the bounds are the same, then skip updating
+ return;
+ }
// Remove any previous pairs
removeSplitPair(taskId1);
removeSplitPair(taskId2);
@@ -121,6 +126,7 @@
mSplitTasks.put(taskId2, taskId1);
mTaskSplitBoundsMap.put(taskId1, splitBounds);
mTaskSplitBoundsMap.put(taskId2, splitBounds);
+ notifyRecentTasksChanged();
}
/**
@@ -133,6 +139,7 @@
mSplitTasks.delete(pairedTaskId);
mTaskSplitBoundsMap.remove(taskId);
mTaskSplitBoundsMap.remove(pairedTaskId);
+ notifyRecentTasksChanged();
}
}
@@ -217,7 +224,7 @@
}
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
- if (pairedTaskId != INVALID_TASK_ID) {
+ if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(pairedTaskId)) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 3d3a630..3cfa541 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -65,32 +65,33 @@
/**
* Starts a task in a stage.
*/
- oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7;
+ oneway void startTask(int taskId, int position, in Bundle options) = 7;
/**
* Starts a shortcut in a stage.
*/
- oneway void startShortcut(String packageName, String shortcutId, int stage, int position,
+ oneway void startShortcut(String packageName, String shortcutId, int position,
in Bundle options, in UserHandle user) = 8;
/**
* Starts an activity in a stage.
*/
- oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage,
- int position, in Bundle options) = 9;
+ oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int position,
+ in Bundle options) = 9;
/**
* Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
- in Bundle sideOptions, int sidePosition, in RemoteTransition remoteTransition) = 10;
+ in Bundle sideOptions, int sidePosition, float splitRatio,
+ in RemoteTransition remoteTransition) = 10;
/**
* Version of startTasks using legacy transition system.
*/
oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
int sideTaskId, in Bundle sideOptions, int sidePosition,
- in RemoteAnimationAdapter adapter) = 11;
+ float splitRatio, in RemoteAnimationAdapter adapter) = 11;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS
new file mode 100644
index 0000000..7237d2b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-modules splitscreen owner
+chenghsiuchang@google.com
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 6b42ed7..0261df4 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
@@ -183,6 +183,15 @@
return mStageCoordinator.isSplitScreenVisible();
}
+ public boolean isTaskInSplitScreen(int taskId) {
+ return isSplitScreenVisible()
+ && mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
+ }
+
+ public @SplitPosition int getSplitPosition(int taskId) {
+ return mStageCoordinator.getSplitPosition(taskId);
+ }
+
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
new WindowContainerTransaction());
@@ -256,9 +265,9 @@
mStageCoordinator.unregisterSplitScreenListener(listener);
}
- public void startTask(int taskId, @SplitScreen.StageType int stage,
- @SplitPosition int position, @Nullable Bundle options) {
- options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+ public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
try {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
@@ -273,10 +282,10 @@
}
}
- public void startShortcut(String packageName, String shortcutId,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) {
- options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
@@ -291,20 +300,18 @@
}
}
- public void startIntent(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
- startIntentLegacy(intent, fillInIntent, stage, position, options);
+ startIntentLegacy(intent, fillInIntent, position, options);
return;
}
- mStageCoordinator.startIntent(intent, fillInIntent, stage, position, options,
+ mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options,
null /* remote */);
}
private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
- @Nullable Bundle options) {
+ @SplitPosition int position, @Nullable Bundle options) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
@@ -338,7 +345,7 @@
};
final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = mStageCoordinator.resolveStartStage(stage, position, options, wct);
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
@@ -595,49 +602,48 @@
}
@Override
- public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
+ public void startTask(int taskId, int position, @Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startTask",
(controller) -> {
- controller.startTask(taskId, stage, position, options);
+ controller.startTask(taskId, position, options);
});
}
@Override
public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
- adapter));
+ splitRatio, adapter));
}
@Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition,
+ @SplitPosition int sidePosition, float splitRatio,
@Nullable RemoteTransition remoteTransition) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
- sideTaskId, sideOptions, sidePosition, remoteTransition));
+ sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition));
}
@Override
- public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ public void startShortcut(String packageName, String shortcutId, int position,
@Nullable Bundle options, UserHandle user) {
executeRemoteCallWithTaskPermission(mController, "startShortcut",
(controller) -> {
- controller.startShortcut(packageName, shortcutId, stage, position,
- options, user);
+ controller.startShortcut(packageName, shortcutId, position, options, user);
});
}
@Override
- public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
+ public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
@Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startIntent",
(controller) -> {
- controller.startIntent(intent, fillInIntent, stage, position, options);
+ controller.startIntent(intent, fillInIntent, position, options);
});
}
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 a3726d4..aaf210d 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
@@ -274,6 +274,17 @@
return mSideStageListener.mVisible && mMainStageListener.mVisible;
}
+ @SplitScreen.StageType
+ int getStageOfTask(int taskId) {
+ if (mMainStage.containsTask(taskId)) {
+ return STAGE_TYPE_MAIN;
+ } else if (mSideStage.containsTask(taskId)) {
+ return STAGE_TYPE_SIDE;
+ }
+
+ return STAGE_TYPE_UNDEFINED;
+ }
+
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitScreen.StageType int stageType,
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
StageTaskListener targetStage;
@@ -322,7 +333,7 @@
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
- @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@Nullable RemoteTransition remoteTransition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -334,6 +345,7 @@
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
addActivityOptions(sideOptions, mSideStage);
@@ -349,7 +361,7 @@
/** Starts 2 tasks in one legacy transition. */
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Need to add another wrapper here in shell so that we can inject the divider bar
// and also manage the process elevation via setRunningRemote
@@ -404,6 +416,7 @@
sideOptions = sideOptions != null ? sideOptions : new Bundle();
setSideStagePosition(sidePosition, wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
@@ -449,12 +462,15 @@
@androidx.annotation.Nullable WindowContainerTransaction wct) {
switch (stage) {
case STAGE_TYPE_UNDEFINED: {
- // Use the stage of the specified position is valid.
if (position != SPLIT_POSITION_UNDEFINED) {
- if (position == getSideStagePosition()) {
- options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
+ if (mMainStage.isActive()) {
+ // Use the stage of the specified position
+ options = resolveStartStage(
+ position == mSideStagePosition ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN,
+ position, options, wct);
} else {
- options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct);
+ // Use the side stage as default to active split screen
+ options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
}
} else {
// Exit split-screen and launch fullscreen since stage wasn't specified.
@@ -665,6 +681,16 @@
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
}
+ @SplitPosition
+ int getSplitPosition(int taskId) {
+ if (mSideStage.getTopVisibleChildTaskId() == taskId) {
+ return getSideStagePosition();
+ } else if (mMainStage.getTopVisibleChildTaskId() == taskId) {
+ return getMainStagePosition();
+ }
+ return SPLIT_POSITION_UNDEFINED;
+ }
+
private void addActivityOptions(Bundle opts, StageTaskListener stage) {
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
}
@@ -768,6 +794,7 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make the stages adjacent to each other so they occlude what's behind them.
wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
mTaskOrganizer.applyTransaction(wct);
}
}
@@ -775,6 +802,7 @@
private void onStageRootTaskVanished(StageListenerImpl stageListener) {
if (stageListener == mMainStageListener || stageListener == mSideStageListener) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
// Deactivate the main stage if it no longer has a root task.
mMainStage.deactivate(wct);
mTaskOrganizer.applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS
new file mode 100644
index 0000000..264e88f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-modules stagesplit owner
+chenghsiuchang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
index 94db9cd..ad2b988 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
@@ -22,6 +22,7 @@
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -208,9 +209,9 @@
mStageCoordinator.unregisterSplitScreenListener(listener);
}
- public void startTask(int taskId, @SplitScreen.StageType int stage,
- @SplitPosition int position, @Nullable Bundle options) {
- options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+ public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
try {
ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
@@ -219,10 +220,10 @@
}
}
- public void startShortcut(String packageName, String shortcutId,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) {
- options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
try {
LauncherApps launcherApps =
@@ -234,20 +235,18 @@
}
}
- public void startIntent(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
+ public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
- startIntentLegacy(intent, fillInIntent, stage, position, options);
+ startIntentLegacy(intent, fillInIntent, position, options);
return;
}
- mStageCoordinator.startIntent(intent, fillInIntent, stage, position, options,
+ mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options,
null /* remote */);
}
private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
- @Nullable Bundle options) {
+ @SplitPosition int position, @Nullable Bundle options) {
LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
@Override
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
@@ -275,7 +274,7 @@
}
};
WindowContainerTransaction wct = new WindowContainerTransaction();
- options = mStageCoordinator.resolveStartStage(stage, position, options, wct);
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
@@ -539,7 +538,7 @@
public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startTask",
(controller) -> {
- controller.startTask(taskId, stage, position, options);
+ controller.startTask(taskId, position, options);
});
}
@@ -568,7 +567,7 @@
@Nullable Bundle options, UserHandle user) {
executeRemoteCallWithTaskPermission(mController, "startShortcut",
(controller) -> {
- controller.startShortcut(packageName, shortcutId, stage, position,
+ controller.startShortcut(packageName, shortcutId, position,
options, user);
});
}
@@ -578,7 +577,7 @@
@Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startIntent",
(controller) -> {
- controller.startIntent(intent, fillInIntent, stage, position, options);
+ controller.startIntent(intent, fillInIntent, position, options);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 5f48c73..014f02b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -72,6 +72,7 @@
private final int mAppRevealDuration;
private final int mAnimationDuration;
private final float mIconStartAlpha;
+ private final float mBrandingStartAlpha;
private final TransactionPool mTransactionPool;
private ValueAnimator mMainAnimator;
@@ -94,9 +95,17 @@
|| iconView.getLayoutParams().height == 0) {
mIconFadeOutDuration = 0;
mIconStartAlpha = 0;
+ mBrandingStartAlpha = 0;
mAppRevealDelay = 0;
} else {
iconView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ // The branding view could only exists when the icon is present.
+ final View brandingView = view.getBrandingView();
+ if (brandingView != null) {
+ mBrandingStartAlpha = brandingView.getAlpha();
+ } else {
+ mBrandingStartAlpha = 0;
+ }
mIconFadeOutDuration = context.getResources().getInteger(
R.integer.starting_window_app_reveal_icon_fade_out_duration);
mAppRevealDelay = context.getResources().getInteger(
@@ -334,13 +343,21 @@
// ignore
}
- private void onAnimationProgress(float linearProgress) {
- View iconView = mSplashScreenView.getIconView();
+ private void onFadeOutProgress(float linearProgress) {
+ final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
+ getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
+ final View iconView = mSplashScreenView.getIconView();
+ final View brandingView = mSplashScreenView.getBrandingView();
if (iconView != null) {
- final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
- getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
iconView.setAlpha(mIconStartAlpha * (1 - iconProgress));
}
+ if (brandingView != null) {
+ brandingView.setAlpha(mBrandingStartAlpha * (1 - iconProgress));
+ }
+ }
+
+ private void onAnimationProgress(float linearProgress) {
+ onFadeOutProgress(linearProgress);
final float revealLinearProgress = getProgress(linearProgress, mAppRevealDelay,
mAppRevealDuration);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index a9c81b3..73f65b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -134,7 +134,8 @@
mDisplayManager.getDisplay(DEFAULT_DISPLAY);
}
- private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
+ @VisibleForTesting
+ final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
/**
* Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
@@ -459,8 +460,23 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"Task start finish, remove starting surface for task: %d",
removalInfo.taskId);
- removeWindowSynced(removalInfo);
+ removeWindowSynced(removalInfo, false /* immediately */);
+ }
+ /**
+ * Clear all starting windows immediately.
+ */
+ public void clearAllWindows() {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Clear all starting windows immediately");
+ final int taskSize = mStartingWindowRecords.size();
+ final int[] taskIds = new int[taskSize];
+ for (int i = taskSize - 1; i >= 0; --i) {
+ taskIds[i] = mStartingWindowRecords.keyAt(i);
+ }
+ for (int i = taskSize - 1; i >= 0; --i) {
+ removeWindowNoAnimate(taskIds[i]);
+ }
}
/**
@@ -542,7 +558,8 @@
return shouldSaveView;
}
- private void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
+ @VisibleForTesting
+ void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
@StartingWindowType int suggestType) {
final StartingWindowRecord tView = new StartingWindowRecord(appToken, view,
null/* TaskSnapshotWindow */, suggestType);
@@ -551,19 +568,18 @@
private void removeWindowNoAnimate(int taskId) {
mTmpRemovalInfo.taskId = taskId;
- removeWindowSynced(mTmpRemovalInfo);
+ removeWindowSynced(mTmpRemovalInfo, true /* immediately */);
}
void onImeDrawnOnTask(int taskId) {
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null && record.mTaskSnapshotWindow != null
&& record.mTaskSnapshotWindow.hasImeSurface()) {
- record.mTaskSnapshotWindow.removeImmediately();
+ removeWindowNoAnimate(taskId);
}
- mStartingWindowRecords.remove(taskId);
}
- protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo) {
+ protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, boolean immediately) {
final int taskId = removalInfo.taskId;
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null) {
@@ -571,7 +587,8 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"Removing splash screen window for task: %d", taskId);
if (record.mContentView != null) {
- if (record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
+ if (immediately
+ || record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
removeWindowInner(record.mDecorView, false);
} else {
if (removalInfo.playRevealAnimation) {
@@ -594,8 +611,12 @@
if (record.mTaskSnapshotWindow != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"Removing task snapshot window for %d", taskId);
- record.mTaskSnapshotWindow.scheduleRemove(
- () -> mStartingWindowRecords.remove(taskId), removalInfo.deferRemoveForIme);
+ if (immediately) {
+ record.mTaskSnapshotWindow.removeImmediately();
+ } else {
+ record.mTaskSnapshotWindow.scheduleRemove(() ->
+ mStartingWindowRecords.remove(taskId), removalInfo.deferRemoveForIme);
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index b62360e..487eb70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -194,6 +194,18 @@
}
/**
+ * Clear all starting window immediately, called this method when releasing the task organizer.
+ */
+ public void clearAllWindows() {
+ mSplashScreenExecutor.execute(() -> {
+ mStartingSurfaceDrawer.clearAllWindows();
+ synchronized (mTaskBackgroundColors) {
+ mTaskBackgroundColors.clear();
+ }
+ });
+ }
+
+ /**
* The interface for calls from outside the Shell, within the host process.
*/
private class StartingSurfaceImpl implements StartingSurface {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
index aadf792..a0c84cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Container of various information needed to display split screen
* tasks/leashes/etc in Launcher
@@ -93,6 +95,24 @@
}
@Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof StagedSplitBounds)) {
+ return false;
+ }
+ // Only need to check the base fields (the other fields are derived from these)
+ final StagedSplitBounds other = (StagedSplitBounds) obj;
+ return Objects.equals(leftTopBounds, other.leftTopBounds)
+ && Objects.equals(rightBottomBounds, other.rightBottomBounds)
+ && leftTopTaskId == other.leftTopTaskId
+ && rightBottomTaskId == other.rightBottomTaskId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(leftTopBounds, rightBottomBounds, leftTopTaskId, rightBottomTaskId);
+ }
+
+ @Override
public String toString() {
return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+ "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 1d463d5..e6c2f38 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.apppairs
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -82,7 +83,7 @@
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @Postsubmit
@Test
override fun statusBarLayerRotatesScales() {
// This test doesn't work in shell transitions because of b/206753786
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 10cf0b7..1a3e42c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.apppairs
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -67,7 +68,7 @@
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @Postsubmit
@Test
override fun statusBarLayerRotatesScales() {
// This test doesn't work in shell transitions because of b/206753786
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index 722ec34..5c78b29 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.apppairs
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -86,7 +87,7 @@
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @Postsubmit
@Test
override fun statusBarLayerRotatesScales() {
// This test doesn't work in shell transitions because of b/206753786
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 38c008c..251d92d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.apppairs
import android.os.SystemClock
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -71,7 +72,7 @@
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @Postsubmit
@Test
override fun statusBarLayerRotatesScales() {
// This test doesn't work in shell transitions because of b/206753786
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 13824b8..d47057f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -16,9 +16,9 @@
package com.android.wm.shell.flicker.apppairs
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -87,7 +87,7 @@
testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
- @FlakyTest
+ @Postsubmit
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index c003084..097867a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -16,9 +16,9 @@
package com.android.wm.shell.flicker.apppairs
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -89,13 +89,13 @@
}
}
- @FlakyTest(bugId = 172776659)
+ @Postsubmit
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
- @FlakyTest(bugId = 172776659)
+ @Postsubmit
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
index 1605d80..a928bbd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.graphics.Point
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
@@ -66,7 +66,7 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
fun testAppIsAlwaysVisible() {
testSpec.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
index a8f17a75..64636be 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.bubble
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -49,7 +49,7 @@
}
}
- @Postsubmit
+ @Presubmit
@Test
fun testAppIsAlwaysVisible() {
testSpec.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 3885155..ef7d65e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -74,7 +73,7 @@
splitScreenApp.component, secondaryApp.component,
FlickerComponentName.SNAPSHOT)
- @Postsubmit
+ @FlakyTest
@Test
fun layerBecomesInvisible() {
testSpec.assertLayers {
@@ -94,11 +93,11 @@
}
}
- @Postsubmit
+ @FlakyTest
@Test
fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
- @Postsubmit
+ @FlakyTest
@Test
fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 079a6ef..c1fba7d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,9 +16,9 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -124,7 +124,7 @@
}
}
- @Postsubmit
+ @FlakyTest
@Test
fun nonResizableAppWindowBecomesVisible() {
testSpec.assertWm {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index a787f2b..77fb101 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,9 +16,9 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -122,11 +122,11 @@
@Test
fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
- @Postsubmit
+ @FlakyTest
@Test
fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible()
- @Postsubmit
+ @FlakyTest
@Test
fun layerBecomesInvisible() {
testSpec.assertLayers {
@@ -136,7 +136,7 @@
}
}
- @Postsubmit
+ @FlakyTest
@Test
fun focusDoesNotChange() {
testSpec.assertEventLog {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 030e040..34c0f849 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -16,8 +16,10 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -25,8 +27,10 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -56,6 +60,8 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) {
+ @get:Rule
+ val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
/**
* Defines the transition used to run the test
@@ -76,6 +82,24 @@
}
}
+ @Postsubmit
+ @Test
+ fun runPresubmitAssertion() {
+ flickerRule.checkPresubmitAssertions()
+ }
+
+ @Postsubmit
+ @Test
+ fun runPostsubmitAssertion() {
+ flickerRule.checkPostsubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runFlakyAssertion() {
+ flickerRule.checkFlakyAssertions()
+ }
+
/** {@inheritDoc} */
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 2def979..1b7220e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -16,8 +16,10 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -25,9 +27,11 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -57,6 +61,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
+ @get:Rule
+ val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
+
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
@@ -65,6 +72,24 @@
}
}
+ @Postsubmit
+ @Test
+ fun runPresubmitAssertion() {
+ flickerRule.checkPresubmitAssertions()
+ }
+
+ @Postsubmit
+ @Test
+ fun runPostsubmitAssertion() {
+ flickerRule.checkPostsubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runFlakyAssertion() {
+ flickerRule.checkFlakyAssertions()
+ }
+
@Before
fun onBefore() {
// This CUJ don't work in shell transitions because of b/204570898 b/204562589
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 9191d0e..40be21a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -72,22 +72,6 @@
}
}
- @Presubmit
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
-
@FlakyTest
@Test
override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
@@ -104,14 +88,6 @@
testSpec.statusBarLayerRotatesScales()
}
- @Presubmit
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
-
- @Presubmit
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 9c26105..a940a7f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -82,19 +83,19 @@
}
}
- @FlakyTest
+ @Postsubmit
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @FlakyTest
+ @Postsubmit
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
- @FlakyTest
+ @Postsubmit
@Test
override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
- @FlakyTest
+ @Postsubmit
@Test
override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
@@ -102,7 +103,7 @@
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @Postsubmit
@Test
override fun statusBarLayerRotatesScales() {
// This test doesn't work in shell transitions because of b/206753786
@@ -110,7 +111,7 @@
super.statusBarLayerRotatesScales()
}
- @FlakyTest
+ @Postsubmit
@Test
fun pipWindowInsideDisplay() {
testSpec.assertWmStart {
@@ -118,7 +119,7 @@
}
}
- @FlakyTest
+ @Postsubmit
@Test
fun pipAppShowsOnTop() {
testSpec.assertWmEnd {
@@ -126,7 +127,7 @@
}
}
- @FlakyTest
+ @Postsubmit
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
@@ -134,13 +135,13 @@
}
}
- @FlakyTest
+ @Postsubmit
@Test
fun pipAlwaysVisible() = testSpec.assertWm {
this.isAppWindowVisible(pipApp.component)
}
- @FlakyTest
+ @Postsubmit
@Test
fun pipAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
@@ -148,7 +149,7 @@
}
}
- @FlakyTest
+ @Postsubmit
@Test
override fun entireScreenCovered() = super.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 73eebad..453050f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -106,6 +106,12 @@
}
@Test
+ public void testSetDivideRatio() {
+ mSplitLayout.setDivideRatio(0.5f);
+ verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
+ }
+
+ @Test
public void testOnDoubleTappedDivider() {
mSplitLayout.onDoubleTappedDivider();
verify(mSplitLayoutHandler).onDoubleTappedDivider();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index bfa2c92..9f74520 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -30,7 +30,9 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -59,8 +61,8 @@
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
-
- mController = new DragAndDropController(mContext, mDisplayController, mUiEventLogger);
+ mController = new DragAndDropController(mContext, mDisplayController, mUiEventLogger,
+ mock(IconProvider.class), mock(ShellExecutor.class));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 734b97b..7fbffc9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -32,8 +32,6 @@
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -149,7 +147,6 @@
mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
ACTIVITY_TYPE_STANDARD);
- setInSplitScreen(false);
setRunningTask(mFullscreenAppTask);
}
@@ -198,10 +195,6 @@
: ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
}
- private void setInSplitScreen(boolean inSplitscreen) {
- doReturn(inSplitscreen).when(mSplitScreenStarter).isSplitScreenVisible();
- }
-
@Test
public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
setRunningTask(mHomeTask);
@@ -211,7 +204,7 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
+ eq(SPLIT_POSITION_UNDEFINED), any());
}
@Test
@@ -223,12 +216,12 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -240,64 +233,12 @@
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
+ eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
- }
-
- @Test
- public void testDragAppOverSplitApp_expectSplitTargets_DropLeft() {
- setInSplitScreen(true);
- setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
- ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
-
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
- }
-
- @Test
- public void testDragAppOverSplitApp_expectSplitTargets_DropRight() {
- setInSplitScreen(true);
- setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
- ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
-
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
- }
-
- @Test
- public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropTop() {
- setInSplitScreen(true);
- setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
- ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
-
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
- }
-
- @Test
- public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropBottom() {
- setInSplitScreen(true);
- setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
- ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
-
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 19a5417..50f6bd7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.mock;
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 java.lang.Integer.MAX_VALUE;
@@ -83,6 +84,36 @@
}
@Test
+ public void testAddRemoveSplitNotifyChange() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ setRawList(t1, t2);
+
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, mock(StagedSplitBounds.class));
+ verify(mRecentTasksController).notifyRecentTasksChanged();
+
+ reset(mRecentTasksController);
+ mRecentTasksController.removeSplitPair(t1.taskId);
+ verify(mRecentTasksController).notifyRecentTasksChanged();
+ }
+
+ @Test
+ public void testAddSameSplitBoundsInfoSkipNotifyChange() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ setRawList(t1, t2);
+
+ // Verify only one update if the split info is the same
+ StagedSplitBounds bounds1 = new StagedSplitBounds(new Rect(0, 0, 50, 50),
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds1);
+ StagedSplitBounds bounds2 = new StagedSplitBounds(new Rect(0, 0, 50, 50),
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds2);
+ verify(mRecentTasksController, times(1)).notifyRecentTasksChanged();
+ }
+
+ @Test
public void testGetRecentTasks() {
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index ef14d84..8128bbe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -21,6 +21,7 @@
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -28,10 +29,13 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -105,7 +109,8 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mStageCoordinator = createStageCoordinator(/* splitLayout */ null);
+ mStageCoordinator = spy(createStageCoordinator(/* splitLayout */ null));
+ doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
@@ -224,6 +229,63 @@
verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
}
+ @Test
+ public void testResolveStartStage_beforeSplitActivated_setsStagePosition() {
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ verify(mStageCoordinator).updateActivityOptions(any(), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT));
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_TOP_OR_LEFT);
+ verify(mStageCoordinator).updateActivityOptions(any(), eq(SPLIT_POSITION_TOP_OR_LEFT));
+ }
+
+ @Test
+ public void testResolveStartStage_afterSplitActivated_retrievesStagePosition() {
+ when(mMainStage.isActive()).thenReturn(true);
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_TOP_OR_LEFT);
+ verify(mStageCoordinator).updateActivityOptions(any(), eq(SPLIT_POSITION_TOP_OR_LEFT));
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getMainStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ verify(mStageCoordinator).updateActivityOptions(any(), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT));
+ }
+
+ @Test
+ public void testResolveStartStage_setsSideStagePosition() {
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getMainStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ }
+
+ @Test
+ public void testResolveStartStage_retrievesStagePosition() {
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_SIDE, SPLIT_POSITION_UNDEFINED,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getSideStagePosition(), SPLIT_POSITION_TOP_OR_LEFT);
+
+ mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN, SPLIT_POSITION_UNDEFINED,
+ null /* options */, null /* wct */);
+ assertEquals(mStageCoordinator.getMainStagePosition(), SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ }
+
private StageCoordinator createStageCoordinator(SplitLayout splitLayout) {
return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 70b7c67..d92b12e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -31,6 +31,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -117,16 +118,19 @@
WindowManager.LayoutParams params, int suggestType) {
// listen for addView
mAddWindowForTask = taskId;
+ saveSplashScreenRecord(appToken, taskId, view, suggestType);
// Do not wait for background color
return false;
}
@Override
- protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo) {
+ protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo,
+ boolean immediately) {
// listen for removeView
if (mAddWindowForTask == removalInfo.taskId) {
mAddWindowForTask = 0;
}
+ mStartingWindowRecords.remove(removalInfo.taskId);
}
}
@@ -179,7 +183,7 @@
removalInfo.taskId = windowInfo.taskInfo.taskId;
mStartingSurfaceDrawer.removeStartingWindow(removalInfo);
waitHandlerIdle(mTestHandler);
- verify(mStartingSurfaceDrawer).removeWindowSynced(any());
+ verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(false));
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
}
@@ -267,11 +271,32 @@
// Verify the task snapshot with IME snapshot will be removed when received the real IME
// drawn callback.
+ // makeTaskSnapshotWindow shall call removeWindowSynced before there add a new
+ // StartingWindowRecord for the task.
mStartingSurfaceDrawer.onImeDrawnOnTask(1);
- verify(mockSnapshotWindow).removeImmediately();
+ verify(mStartingSurfaceDrawer, times(2))
+ .removeWindowSynced(any(), eq(true));
}
}
+ @Test
+ public void testClearAllWindows() {
+ final int taskId = 1;
+ final StartingWindowInfo windowInfo =
+ createWindowInfo(taskId, android.R.style.Theme);
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
+ STARTING_WINDOW_TYPE_SPLASH_SCREEN);
+ waitHandlerIdle(mTestHandler);
+ verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(),
+ eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
+ assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
+
+ mStartingSurfaceDrawer.clearAllWindows();
+ waitHandlerIdle(mTestHandler);
+ verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(true));
+ assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0);
+ }
+
private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
StartingWindowInfo windowInfo = new StartingWindowInfo();
final ActivityInfo info = new ActivityInfo();
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index d17c328..bbd4c81 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -641,22 +641,25 @@
}
// Retrieve all the resource ids belonging to this policy chunk
- std::unordered_set<uint32_t> ids;
const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>();
const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
+ std::unordered_set<uint32_t> ids;
+ ids.reserve(ids_end - ids_begin);
for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
if (!id_iter) {
+ LOG(ERROR) << "NULL ResTable_ref record??";
return {};
}
ids.insert(dtohl(id_iter->ident));
}
// Add the pairing of overlayable properties and resource ids to the package
- OverlayableInfo overlayable_info{};
- overlayable_info.name = name;
- overlayable_info.actor = actor;
- overlayable_info.policy_flags = policy_header->policy_flags;
- loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids);
+ OverlayableInfo overlayable_info {
+ .name = name,
+ .actor = actor,
+ .policy_flags = policy_header->policy_flags
+ };
+ loaded_package->overlayable_infos_.emplace_back(std::move(overlayable_info), std::move(ids));
loaded_package->defines_overlayable_ = true;
break;
}
@@ -683,15 +686,23 @@
break;
}
- std::unordered_set<uint32_t> finalized_ids;
const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
if (!lib_alias) {
+ LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small.";
+ return {};
+ }
+ if ((child_chunk.data_size() / sizeof(ResTable_staged_alias_entry))
+ < dtohl(lib_alias->count)) {
+ LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small to hold entries.";
return {};
}
const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
const auto entry_end = entry_begin + dtohl(lib_alias->count);
+ std::unordered_set<uint32_t> finalized_ids;
+ finalized_ids.reserve(entry_end - entry_begin);
for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
if (!entry_iter) {
+ LOG(ERROR) << "NULL ResTable_staged_alias_entry record??";
return {};
}
auto finalized_id = dtohl(entry_iter->finalizedResId);
@@ -702,8 +713,7 @@
}
auto staged_id = dtohl(entry_iter->stagedResId);
- auto [_, success] = loaded_package->alias_id_map_.insert(std::make_pair(staged_id,
- finalized_id));
+ auto [_, success] = loaded_package->alias_id_map_.emplace(staged_id, finalized_id);
if (!success) {
LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
staged_id);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 491f5f5..a2d0103 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -107,6 +107,8 @@
target: {
android: {
shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ "android.hardware.graphics.common@1.2",
"liblog",
"libcutils",
"libutils",
@@ -126,9 +128,11 @@
static_libs: [
"libEGL_blobCache",
"libprotoutil",
+ "libshaders",
"libstatslog_hwui",
"libstatspull_lazy",
"libstatssocket_lazy",
+ "libtonemap",
],
},
host: {
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 0b3b393..a5c0924 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -20,9 +20,11 @@
// TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead.
#include <surfacetexture/surface_texture_platform.h>
+
#include "AutoBackendTextureRelease.h"
#include "Matrix.h"
#include "Properties.h"
+#include "android/hdr_metadata.h"
#include "renderstate/RenderState.h"
#include "renderthread/EglManager.h"
#include "renderthread/RenderThread.h"
@@ -147,6 +149,9 @@
mUpdateTexImage = false;
float transformMatrix[16];
android_dataspace dataspace;
+ AHdrMetadataType hdrMetadataType;
+ android_cta861_3_metadata cta861_3;
+ android_smpte2086_metadata smpte2086;
int slot;
bool newContent = false;
ARect currentCrop;
@@ -155,8 +160,9 @@
// is necessary if the SurfaceTexture queue is in synchronous mode, and we
// cannot tell which mode it is in.
AHardwareBuffer* hardwareBuffer = ASurfaceTexture_dequeueBuffer(
- mSurfaceTexture.get(), &slot, &dataspace, transformMatrix, &outTransform,
- &newContent, createReleaseFence, fenceWait, this, ¤tCrop);
+ mSurfaceTexture.get(), &slot, &dataspace, &hdrMetadataType, &cta861_3,
+ &smpte2086, transformMatrix, &outTransform, &newContent, createReleaseFence,
+ fenceWait, this, ¤tCrop);
if (hardwareBuffer) {
mCurrentSlot = slot;
@@ -173,7 +179,18 @@
SkRect currentCropRect =
SkRect::MakeLTRB(currentCrop.left, currentCrop.top, currentCrop.right,
currentCrop.bottom);
- updateLayer(forceFilter, layerImage, outTransform, currentCropRect);
+
+ float maxLuminanceNits = -1.f;
+ if (hdrMetadataType & HDR10_SMPTE2086) {
+ maxLuminanceNits = std::max(smpte2086.maxLuminance, maxLuminanceNits);
+ }
+
+ if (hdrMetadataType & HDR10_CTA861_3) {
+ maxLuminanceNits =
+ std::max(cta861_3.maxContentLightLevel, maxLuminanceNits);
+ }
+ updateLayer(forceFilter, layerImage, outTransform, currentCropRect,
+ maxLuminanceNits);
}
}
}
@@ -186,13 +203,15 @@
}
void DeferredLayerUpdater::updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage,
- const uint32_t transform, SkRect currentCrop) {
+ const uint32_t transform, SkRect currentCrop,
+ float maxLuminanceNits) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
mLayer->setCurrentCropRect(currentCrop);
mLayer->setWindowTransform(transform);
mLayer->setImage(layerImage);
+ mLayer->setMaxLuminanceNits(maxLuminanceNits);
}
void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index da8041f..9a4c550 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -91,7 +91,7 @@
void detachSurfaceTexture();
void updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage, const uint32_t transform,
- SkRect currentCrop);
+ SkRect currentCrop, float maxLuminanceNits = -1.f);
void destroyLayer();
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 0789344..47eb5d3 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -96,6 +96,12 @@
inline sk_sp<SkImage> getImage() const { return this->layerImage; }
+ inline void setMaxLuminanceNits(float maxLuminanceNits) {
+ mMaxLuminanceNits = maxLuminanceNits;
+ }
+
+ inline float getMaxLuminanceNits() { return mMaxLuminanceNits; }
+
void draw(SkCanvas* canvas);
protected:
@@ -158,6 +164,11 @@
*/
bool mBlend = false;
+ /**
+ * Max input luminance if the layer is HDR
+ */
+ float mMaxLuminanceNits = -1;
+
}; // struct Layer
} // namespace uirenderer
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 5aad821..6fc251d 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -118,6 +118,24 @@
}
}
+bool WebViewFunctor::prepareRootSurfaceControl() {
+ if (!Properties::enableWebViewOverlays) return false;
+
+ renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
+ if (!activeContext) return false;
+
+ ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ if (!rootSurfaceControl) return false;
+
+ int32_t rgid = activeContext->getSurfaceControlGenerationId();
+ if (mParentSurfaceControlGenerationId != rgid) {
+ reparentSurfaceControl(rootSurfaceControl);
+ mParentSurfaceControlGenerationId = rgid;
+ }
+
+ return true;
+}
+
void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
ATRACE_NAME("WebViewFunctor::drawGl");
if (!mHasContext) {
@@ -131,20 +149,8 @@
.mergeTransaction = currentFunctor.mergeTransaction,
};
- if (Properties::enableWebViewOverlays && !drawInfo.isLayer) {
- renderthread::CanvasContext* activeContext =
- renderthread::CanvasContext::getActiveContext();
- if (activeContext != nullptr) {
- ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
- if (rootSurfaceControl) {
- overlayParams.overlaysMode = OverlaysMode::Enabled;
- int32_t rgid = activeContext->getSurfaceControlGenerationId();
- if (mParentSurfaceControlGenerationId != rgid) {
- reparentSurfaceControl(rootSurfaceControl);
- mParentSurfaceControlGenerationId = rgid;
- }
- }
- }
+ if (!drawInfo.isLayer && prepareRootSurfaceControl()) {
+ overlayParams.overlaysMode = OverlaysMode::Enabled;
}
mCallbacks.gles.draw(mFunctor, mData, drawInfo, overlayParams);
@@ -170,7 +176,10 @@
.mergeTransaction = currentFunctor.mergeTransaction,
};
- // TODO, enable surface control once offscreen mode figured out
+ if (!params.is_layer && prepareRootSurfaceControl()) {
+ overlayParams.overlaysMode = OverlaysMode::Enabled;
+ }
+
mCallbacks.vk.draw(mFunctor, mData, params, overlayParams);
}
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index f28f310..0a02f2d 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -88,6 +88,7 @@
}
private:
+ bool prepareRootSurfaceControl();
void reparentSurfaceControl(ASurfaceControl* parent);
private:
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 1439656..553b08f 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -15,12 +15,19 @@
*/
#include "LayerDrawable.h"
+
+#include <shaders/shaders.h>
+#include <utils/Color.h>
#include <utils/MathUtils.h>
+#include "DeviceInfo.h"
#include "GrBackendSurface.h"
#include "SkColorFilter.h"
+#include "SkRuntimeEffect.h"
#include "SkSurface.h"
#include "gl/GrGLTypes.h"
+#include "math/mat4.h"
+#include "system/graphics-base-v1.0.h"
#include "system/window.h"
namespace android {
@@ -69,6 +76,35 @@
isIntegerAligned(dstDevRect.y()));
}
+static sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
+ const shaders::LinearEffect& linearEffect,
+ float maxDisplayLuminance, float maxLuminance) {
+ auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
+ auto [runtimeEffect, error] = SkRuntimeEffect::MakeForShader(std::move(shaderString));
+ if (!runtimeEffect) {
+ LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+ }
+
+ SkRuntimeShaderBuilder effectBuilder(std::move(runtimeEffect));
+
+ effectBuilder.child("child") = std::move(shader);
+
+ const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, mat4(),
+ maxDisplayLuminance, maxLuminance);
+
+ for (const auto& uniform : uniforms) {
+ effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
+ }
+
+ return effectBuilder.makeShader(nullptr, false);
+}
+
+static bool isHdrDataspace(ui::Dataspace dataspace) {
+ const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+ return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
// TODO: Context arg probably doesn't belong here – do debug check at callsite instead.
bool LayerDrawable::DrawLayer(GrRecordingContext* context,
SkCanvas* canvas,
@@ -150,8 +186,30 @@
sampling = SkSamplingOptions(SkFilterMode::kLinear);
}
- canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
- constraint);
+ const auto sourceDataspace = static_cast<ui::Dataspace>(
+ ColorSpaceToADataSpace(layerImage->colorSpace(), layerImage->colorType()));
+ const SkImageInfo& imageInfo = canvas->imageInfo();
+ const auto destinationDataspace = static_cast<ui::Dataspace>(
+ ColorSpaceToADataSpace(imageInfo.colorSpace(), imageInfo.colorType()));
+
+ if (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace)) {
+ const auto effect = shaders::LinearEffect{
+ .inputDataspace = sourceDataspace,
+ .outputDataspace = destinationDataspace,
+ .undoPremultipliedAlpha = layerImage->alphaType() == kPremul_SkAlphaType,
+ .fakeInputDataspace = destinationDataspace};
+ auto shader = layerImage->makeShader(sampling,
+ SkMatrix::RectToRect(skiaSrcRect, skiaDestRect));
+ constexpr float kMaxDisplayBrightess = 1000.f;
+ shader = createLinearEffectShader(std::move(shader), effect, kMaxDisplayBrightess,
+ layer->getMaxLuminanceNits());
+ paint.setShader(shader);
+ canvas->drawRect(skiaDestRect, paint);
+ } else {
+ canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
+ constraint);
+ }
+
canvas->restore();
// restore the original matrix
if (useLayerTransform) {
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 8abf4534..e6ef95b 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -72,6 +72,7 @@
.clip_top = mClip.fTop,
.clip_right = mClip.fRight,
.clip_bottom = mClip.fBottom,
+ .is_layer = !vulkan_info.fFromSwapchainOrAndroidWindow,
};
mat4.getColMajor(¶ms.transform[0]);
params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer;
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
index 4ae0f5a..5c59657 100644
--- a/libs/hwui/private/hwui/DrawVkInfo.h
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -68,6 +68,9 @@
int clip_top;
int clip_right;
int clip_bottom;
+
+ // Input: Whether destination surface is offscreen surface.
+ bool is_layer;
};
} // namespace uirenderer
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index fe9a30a..611a4d9 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -426,7 +426,7 @@
if (bufferInfo->skSurface.get() == nullptr) {
bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer(
mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
- kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr);
+ kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr, /*from_window=*/true);
if (bufferInfo->skSurface.get() == nullptr) {
ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer,
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 8f04cfb..f43586f 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -17,24 +17,29 @@
#define LOG_TAG "PointerController"
//#define LOG_NDEBUG 0
-// Log debug messages about pointer updates
-#define DEBUG_POINTER_UPDATES 0
-
#include "PointerController.h"
-#include "MouseCursorController.h"
#include "PointerControllerContext.h"
-#include "TouchSpotController.h"
-#include <log/log.h>
-
-#include <SkBitmap.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
-#include <SkPaint.h>
namespace android {
+namespace {
+
+const ui::Transform kIdentityTransform;
+
+} // namespace
+
+// --- PointerController::DisplayInfoListener ---
+
+void PointerController::DisplayInfoListener::onWindowInfosChanged(
+ const std::vector<android::gui::WindowInfo>&,
+ const std::vector<android::gui::DisplayInfo>& displayInfo) {
+ mPointerController.onDisplayInfosChanged(displayInfo);
+}
+
// --- PointerController ---
std::shared_ptr<PointerController> PointerController::create(
@@ -63,9 +68,16 @@
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper,
const sp<SpriteController>& spriteController)
- : mContext(policy, looper, spriteController, *this), mCursorController(mContext) {
+ : mContext(policy, looper, spriteController, *this),
+ mCursorController(mContext),
+ mDisplayInfoListener(new DisplayInfoListener(*this)) {
std::scoped_lock lock(mLock);
mLocked.presentation = Presentation::SPOT;
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(mDisplayInfoListener);
+}
+
+PointerController::~PointerController() {
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(mDisplayInfoListener);
}
bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
@@ -74,7 +86,14 @@
}
void PointerController::move(float deltaX, float deltaY) {
- mCursorController.move(deltaX, deltaY);
+ const int32_t displayId = mCursorController.getDisplayId();
+ vec2 transformed;
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
+ }
+ mCursorController.move(transformed.x, transformed.y);
}
void PointerController::setButtonState(int32_t buttonState) {
@@ -86,12 +105,26 @@
}
void PointerController::setPosition(float x, float y) {
- std::scoped_lock lock(mLock);
- mCursorController.setPosition(x, y);
+ const int32_t displayId = mCursorController.getDisplayId();
+ vec2 transformed;
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transform.transform(x, y);
+ }
+ mCursorController.setPosition(transformed.x, transformed.y);
}
void PointerController::getPosition(float* outX, float* outY) const {
+ const int32_t displayId = mCursorController.getDisplayId();
mCursorController.getPosition(outX, outY);
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ const auto xy = transform.inverse().transform(*outX, *outY);
+ *outX = xy.x;
+ *outY = xy.y;
+ }
}
int32_t PointerController::getDisplayId() const {
@@ -130,11 +163,25 @@
void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits, int32_t displayId) {
std::scoped_lock lock(mLock);
+ std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
+ const ui::Transform& transform = getTransformForDisplayLocked(displayId);
+
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
+
+ const vec2 xy = transform.transform(spotCoords[index].getXYValue());
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ }
+
auto it = mLocked.spotControllers.find(displayId);
if (it == mLocked.spotControllers.end()) {
mLocked.spotControllers.try_emplace(displayId, displayId, mContext);
}
- mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits);
+ mLocked.spotControllers.at(displayId).setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits);
}
void PointerController::clearSpots() {
@@ -194,7 +241,7 @@
void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
std::unordered_set<int32_t> displayIdSet;
- for (DisplayViewport viewport : viewports) {
+ for (const DisplayViewport& viewport : viewports) {
displayIdSet.insert(viewport.displayId);
}
@@ -214,4 +261,17 @@
}
}
+void PointerController::onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfo) {
+ std::scoped_lock lock(mLock);
+ mLocked.mDisplayInfos = displayInfo;
+}
+
+const ui::Transform& PointerController::getTransformForDisplayLocked(int displayId) const {
+ const auto& di = mLocked.mDisplayInfos;
+ auto it = std::find_if(di.begin(), di.end(), [displayId](const gui::DisplayInfo& info) {
+ return info.displayId == displayId;
+ });
+ return it != di.end() ? it->transform : kIdentityTransform;
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 97567ba..796077f 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,7 @@
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
const sp<SpriteController>& spriteController);
- virtual ~PointerController() = default;
+ ~PointerController() override;
virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
virtual void move(float deltaX, float deltaY);
@@ -72,6 +72,8 @@
void reloadPointerResources();
void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
+ void onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfos);
+
private:
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
@@ -85,9 +87,23 @@
struct Locked {
Presentation presentation;
+ std::vector<gui::DisplayInfo> mDisplayInfos;
std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
} mLocked GUARDED_BY(mLock);
+ class DisplayInfoListener : public gui::WindowInfosListener {
+ public:
+ explicit DisplayInfoListener(PointerController& pc) : mPointerController(pc){};
+ void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>&,
+ const std::vector<android::gui::DisplayInfo>&) override;
+
+ private:
+ PointerController& mPointerController;
+ };
+ sp<DisplayInfoListener> mDisplayInfoListener;
+
+ const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(mLock);
+
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
const sp<SpriteController>& spriteController);
void clearSpotsLocked();
diff --git a/lint-baseline.xml b/lint-baseline.xml
new file mode 100644
index 0000000..79b2155
--- /dev/null
+++ b/lint-baseline.xml
@@ -0,0 +1,565 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev">
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" final String component = Settings.Secure.getString(getContentResolver(),"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java"
+ line="60"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Settings.Secure.getInt(context.getContentResolver(),"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java"
+ line="188"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Settings.Secure.getInt(context.getContentResolver(),"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java"
+ line="194"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Settings.Secure.getInt(context.getContentResolver(),"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/app/AssistUtils.java"
+ line="216"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
+ errorLine1=" * volume, false otherwise."
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/AudioManager.java"
+ line="1028"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" public final boolean isEnabled() {"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="70"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" public final String getRawLocale() {"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="81"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getFloat()` called from system process. Please call `android.provider.Settings.Secure#getFloatForUser()` instead. "
+ errorLine1=" public final float getFontScale() {"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="111"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" public int getRawUserStyle() {"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="120"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" final int foregroundColor = Secure.getInt("
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="478"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" final int backgroundColor = Secure.getInt("
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="480"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" final int edgeType = Secure.getInt("
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="482"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" final int edgeColor = Secure.getInt("
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="484"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" final int windowColor = Secure.getInt("
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="486"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" String rawTypeface = Secure.getString(cr, Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE);"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/CaptioningManager.java"
+ line="489"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" String nearbyComponent = Settings.Secure.getString("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/app/ChooserActivity.java"
+ line="1156"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" final ContentResolver cr = context.getContentResolver();"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/display/ColorDisplayManager.java"
+ line="587"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Secure.getInt(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0) == 1"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/display/ColorDisplayManager.java"
+ line="588"
+ column="68"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" int inversionEnabled = Settings.Secure.getInt(contentResolver,"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/security/ConfirmationPrompt.java"
+ line="220"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getFloat()` called from system process. Please call `android.provider.Settings.System#getFloatForUser()` instead. "
+ errorLine1=" float fontScale = Settings.System.getFloat(contentResolver,"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/security/ConfirmationPrompt.java"
+ line="225"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" int a11yEnabled = Settings.Secure.getInt(contentResolver,"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/security/ConfirmationPrompt.java"
+ line="237"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" Secure.getInt(resolver, Secure.RELEASE_COMPRESS_BLOCKS_ON_INSTALL, 1) != 0;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/content/F2fsUtils.java"
+ line="96"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
+ errorLine1=" int speed = DEFAULT_POINTER_SPEED;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/input/InputManager.java"
+ line="865"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" final ContentResolver contentResolver = fallbackContext.getContentResolver();"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java"
+ line="2860"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/inputmethodservice/InputMethodService.java"
+ line="1205"
+ column="79"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" if (showImeWithHardKeyboardUri.equals(uri)) {"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/inputmethodservice/InputMethodService.java"
+ line="1225"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" RemoteViews.MARGIN_BOTTOM, 0);"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/Notification.java"
+ line="5619"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Settings.Secure.getInt(context.getContentResolver(),"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java"
+ line="40"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Settings.Secure.getInt(mContext.getContentResolver(),"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java"
+ line="328"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" boolean isTvSetupComplete = Settings.Secure.getInt(getContext().getContentResolver(),"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java"
+ line="3129"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" isTvSetupComplete &= Settings.Secure.getInt(getContext().getContentResolver(),"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java"
+ line="3131"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getString()` called from system process. Please call `android.provider.Settings.System#getStringForUser()` instead. "
+ errorLine1=" final String touchDataJson = Settings.System.getString("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/app/PlatLogoActivity.java"
+ line="184"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) == 1;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java"
+ line="463"
+ column="8"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" String comp = Settings.Secure.getString(cr, Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java"
+ line="97"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
+ errorLine1=" final String setting = getDefaultRingtoneSetting(type);"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/RingtoneManager.java"
+ line="1105"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" final String targetString = Settings.Secure.getString(context.getContentResolver(),"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/accessibility/util/ShortcutUtils.java"
+ line="55"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" final String targetsValue = Settings.Secure.getString(context.getContentResolver(),"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/accessibility/util/ShortcutUtils.java"
+ line="82"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" final String targetString = Settings.Secure.getString(context.getContentResolver(),"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/accessibility/util/ShortcutUtils.java"
+ line="112"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/speech/SpeechRecognizer.java"
+ line="665"
+ column="3"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
+ errorLine1=" boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/method/TextKeyListener.java"
+ line="296"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
+ errorLine1=" boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/method/TextKeyListener.java"
+ line="297"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
+ errorLine1=" boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/method/TextKeyListener.java"
+ line="298"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. "
+ errorLine1=" boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/method/TextKeyListener.java"
+ line="299"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Settings.Secure.getInt(getContentResolver(), name, defaultValue);"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/speech/tts/TextToSpeechService.java"
+ line="422"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" return Settings.Secure.getInt(getContext().getContentResolver(),"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java"
+ line="63"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" String engine = getString(mContext.getContentResolver(),"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/speech/tts/TtsEngines.java"
+ line="116"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE));"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/speech/tts/TtsEngines.java"
+ line="337"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE),"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/speech/tts/TtsEngines.java"
+ line="373"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" final String prefList = Settings.Secure.getString(mContext.getContentResolver(),"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/speech/tts/TtsEngines.java"
+ line="527"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
+ errorLine1=" }"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/service/autofill/UserData.java"
+ line="535"
+ column="10"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
+ errorLine1=" public static boolean isActiveService(Context context, ComponentName service) {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/voice/VoiceInteractionService.java"
+ line="156"
+ column="74"/>
+ </issue>
+
+</issues>
diff --git a/media/Android.bp b/media/Android.bp
index 15b24b2..fcdfd72 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -115,6 +115,7 @@
aidl_interface {
name: "android.media.soundtrigger.types",
vendor_available: true,
+ host_supported: true,
flags: [
"-Werror",
"-Weverything",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 2916b01..337b45c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -62,6 +62,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -1021,6 +1022,29 @@
}
/**
+ * Returns the current user setting for ramping ringer on incoming phone call ringtone.
+ *
+ * @return true if the incoming phone call ringtone is configured to gradually increase its
+ * volume, false otherwise.
+ */
+ public boolean isRampingRingerEnabled() {
+ return Settings.System.getInt(getContext().getContentResolver(),
+ Settings.System.APPLY_RAMPING_RINGER, 0) != 0;
+ }
+
+ /**
+ * Sets the flag for enabling ramping ringer on incoming phone call ringtone.
+ *
+ * @see #isRampingRingerEnabled()
+ * @hide
+ */
+ @TestApi
+ public void setRampingRingerEnabled(boolean enabled) {
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.APPLY_RAMPING_RINGER, enabled ? 1 : 0);
+ }
+
+ /**
* Checks valid ringer mode values.
*
* @return true if the ringer mode indicated is valid, false otherwise.
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index bac44ad..e979a1b5 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -163,6 +163,14 @@
* {@link android.graphics.BitmapFactory#decodeByteArray BitmapFactory#decodeByteArray}.
* </td>
* </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#YCBCR_P010 YCBCR_P010}</td>
+ * <td>1</td>
+ * <td>P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
+ * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit
+ * little-endian value, with the lower 6 bits set to zero.
+ * </td>
+ * </tr>
* </table>
*
* @see android.graphics.ImageFormat
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 4daedfc..939b679 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -5106,7 +5106,6 @@
public MediaImage(
@NonNull ByteBuffer buffer, @NonNull ByteBuffer info, boolean readOnly,
long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
- mFormat = ImageFormat.YUV_420_888;
mTimestamp = timestamp;
mIsImageValid = true;
mIsReadOnly = buffer.isReadOnly();
@@ -5119,6 +5118,11 @@
mBufferContext = 0;
+ int cbPlaneOffset = -1;
+ int crPlaneOffset = -1;
+ int planeOffsetInc = -1;
+ int pixelStride = -1;
+
// read media-info. See MediaImage2
if (info.remaining() == 104) {
int type = info.getInt();
@@ -5136,14 +5140,27 @@
"unsupported size: " + mWidth + "x" + mHeight);
}
int bitDepth = info.getInt();
- if (bitDepth != 8) {
+ if (bitDepth != 8 && bitDepth != 10) {
throw new UnsupportedOperationException("unsupported bit depth: " + bitDepth);
}
int bitDepthAllocated = info.getInt();
- if (bitDepthAllocated != 8) {
+ if (bitDepthAllocated != 8 && bitDepthAllocated != 16) {
throw new UnsupportedOperationException(
"unsupported allocated bit depth: " + bitDepthAllocated);
}
+ if (bitDepth == 8 && bitDepthAllocated == 8) {
+ mFormat = ImageFormat.YUV_420_888;
+ planeOffsetInc = 1;
+ pixelStride = 2;
+ } else if (bitDepth == 10 && bitDepthAllocated == 16) {
+ mFormat = ImageFormat.YCBCR_P010;
+ planeOffsetInc = 2;
+ pixelStride = 4;
+ } else {
+ throw new UnsupportedOperationException("couldn't infer ImageFormat"
+ + " bitDepth: " + bitDepth + " bitDepthAllocated: " + bitDepthAllocated);
+ }
+
mPlanes = new MediaPlane[numPlanes];
for (int ix = 0; ix < numPlanes; ix++) {
int planeOffset = info.getInt();
@@ -5165,12 +5182,31 @@
buffer.limit(buffer.position() + Utils.divUp(bitDepth, 8)
+ (mHeight / vert - 1) * rowInc + (mWidth / horiz - 1) * colInc);
mPlanes[ix] = new MediaPlane(buffer.slice(), rowInc, colInc);
+ if ((mFormat == ImageFormat.YUV_420_888 || mFormat == ImageFormat.YCBCR_P010)
+ && ix == 1) {
+ cbPlaneOffset = planeOffset;
+ } else if ((mFormat == ImageFormat.YUV_420_888
+ || mFormat == ImageFormat.YCBCR_P010) && ix == 2) {
+ crPlaneOffset = planeOffset;
+ }
}
} else {
throw new UnsupportedOperationException(
"unsupported info length: " + info.remaining());
}
+ // Validate chroma semiplanerness.
+ if (mFormat == ImageFormat.YCBCR_P010) {
+ if (crPlaneOffset != cbPlaneOffset + planeOffsetInc) {
+ throw new UnsupportedOperationException("Invalid plane offsets"
+ + " cbPlaneOffset: " + cbPlaneOffset + " crPlaneOffset: " + crPlaneOffset);
+ }
+ if (mPlanes[1].getPixelStride() != pixelStride
+ || mPlanes[2].getPixelStride() != pixelStride) {
+ throw new UnsupportedOperationException("Invalid pixelStride");
+ }
+ }
+
if (cropRect == null) {
cropRect = new Rect(0, 0, mWidth, mHeight);
}
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java
index 6077f04..4d8eda1 100644
--- a/media/java/android/media/tv/BroadcastInfoRequest.java
+++ b/media/java/android/media/tv/BroadcastInfoRequest.java
@@ -22,12 +22,21 @@
import android.annotation.NonNull;
/** @hide */
-public final class BroadcastInfoRequest implements Parcelable {
+public abstract class BroadcastInfoRequest implements Parcelable {
+ protected static final int PARCEL_TOKEN_TS_REQUEST = 1;
+
public static final @NonNull Parcelable.Creator<BroadcastInfoRequest> CREATOR =
new Parcelable.Creator<BroadcastInfoRequest>() {
@Override
public BroadcastInfoRequest createFromParcel(Parcel source) {
- return new BroadcastInfoRequest(source);
+ int token = source.readInt();
+ switch (token) {
+ case PARCEL_TOKEN_TS_REQUEST:
+ return TsRequest.createFromParcelBody(source);
+ default:
+ throw new IllegalStateException(
+ "Unexpected broadcast info request type token in parcel.");
+ }
}
@Override
@@ -42,10 +51,14 @@
this.requestId = requestId;
}
- private BroadcastInfoRequest(Parcel source) {
+ protected BroadcastInfoRequest(Parcel source) {
requestId = source.readInt();
}
+ public int getRequestId() {
+ return requestId;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java
index 64c884e..fe4e8b7 100644
--- a/media/java/android/media/tv/BroadcastInfoResponse.java
+++ b/media/java/android/media/tv/BroadcastInfoResponse.java
@@ -46,6 +46,10 @@
requestId = source.readInt();
}
+ public int getRequestId() {
+ return requestId;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/media/java/android/media/tv/TsRequest.aidl
similarity index 77%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to media/java/android/media/tv/TsRequest.aidl
index 3729c09..0abc7ec 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/media/java/android/media/tv/TsRequest.aidl
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package android.window;
+package android.media.tv;
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+parcelable TsRequest;
diff --git a/media/java/android/media/tv/TsRequest.java b/media/java/android/media/tv/TsRequest.java
new file mode 100644
index 0000000..3690d05
--- /dev/null
+++ b/media/java/android/media/tv/TsRequest.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class TsRequest extends BroadcastInfoRequest implements Parcelable {
+ public static final @NonNull Parcelable.Creator<TsRequest> CREATOR =
+ new Parcelable.Creator<TsRequest>() {
+ @Override
+ public TsRequest createFromParcel(Parcel source) {
+ source.readInt();
+ return createFromParcelBody(source);
+ }
+
+ @Override
+ public TsRequest[] newArray(int size) {
+ return new TsRequest[size];
+ }
+ };
+
+ int tsPid;
+
+ public static TsRequest createFromParcelBody(Parcel in) {
+ return new TsRequest(in);
+ }
+
+ public TsRequest(int requestId, int tsPid) {
+ super(requestId);
+ this.tsPid = tsPid;
+ }
+
+ protected TsRequest(Parcel source) {
+ super(source);
+ tsPid = source.readInt();
+ }
+
+ public int getTsPid() {
+ return tsPid;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(PARCEL_TOKEN_TS_REQUEST);
+ super.writeToParcel(dest, flags);
+ dest.writeInt(tsPid);
+ }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 0461f0a..bafb03b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -657,13 +657,6 @@
*/
void onError(Session session, @TvInputManager.RecordingError int error) {
}
-
- /**
- * @param session
- * @param response
- */
- public void onBroadcastInfoResponse(Session session, BroadcastInfoResponse response) {
- }
}
private static final class SessionCallbackRecord {
@@ -848,7 +841,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- mSessionCallback.onBroadcastInfoResponse(mSession, response);
+ mSession.getIAppSession().notifyBroadcastInfoResponse(response);
}
});
}
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 0dd64b8..39c438a 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -15,6 +15,9 @@
*/
package android.media.tv.interactive;
+import android.media.tv.BroadcastInfoRequest;
+
+import android.view.InputChannel;
/**
* Interface a client of the ITvIAppManager implements, to identify itself and receive information
@@ -22,7 +25,8 @@
* @hide
*/
oneway interface ITvIAppClient {
- void onSessionCreated(in String iAppServiceId, IBinder token, int seq);
+ void onSessionCreated(in String iAppServiceId, IBinder token, in InputChannel channel, int seq);
void onSessionReleased(int seq);
void onLayoutSurface(int left, int top, int right, int bottom, int seq);
+ void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index 104efe6..25e1ace 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -19,6 +19,7 @@
import android.media.tv.interactive.ITvIAppClient;
import android.media.tv.interactive.ITvIAppManagerCallback;
import android.media.tv.interactive.TvIAppInfo;
+import android.media.tv.BroadcastInfoResponse;
import android.view.Surface;
/**
@@ -34,6 +35,8 @@
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
int userId);
+ void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response,
+ int UserId);
void registerCallback(in ITvIAppManagerCallback callback, int userId);
void unregisterCallback(in ITvIAppManagerCallback callback, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
index c4f82eb..1dee9cc 100644
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl
@@ -16,7 +16,9 @@
package android.media.tv.interactive;
+import android.media.tv.interactive.ITvIAppServiceCallback;
import android.media.tv.interactive.ITvIAppSessionCallback;
+import android.view.InputChannel;
/**
* Top-level interface to a TV IApp component (implemented in a Service). It's used for
@@ -24,5 +26,8 @@
* @hide
*/
oneway interface ITvIAppService {
- void createSession(in ITvIAppSessionCallback callback, in String iAppServiceId, int type);
+ void registerCallback(in ITvIAppServiceCallback callback);
+ void unregisterCallback(in ITvIAppServiceCallback callback);
+ void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
+ in String iAppServiceId, int type);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
index 0afa971..440b3d30 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.view.Surface;
+import android.media.tv.BroadcastInfoResponse;
/**
* Sub-interface of ITvIAppService.aidl which is created per session and has its own context.
@@ -27,4 +28,5 @@
void release();
void setSurface(in Surface surface);
void dispatchSurfaceChanged(int format, int width, int height);
+ void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index 0873aad..d308463 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.media.tv.interactive.ITvIAppSession;
+import android.media.tv.BroadcastInfoRequest;
/**
* Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when
@@ -26,4 +27,5 @@
oneway interface ITvIAppSessionCallback {
void onSessionCreated(in ITvIAppSession session);
void onLayoutSurface(int left, int top, int right, int bottom);
+ void onBroadcastInfoRequest(in BroadcastInfoRequest request);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index 093e1be..ae35edc 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -20,12 +20,20 @@
import android.annotation.Nullable;
import android.annotation.SystemService;
import android.content.Context;
+import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvInputManager;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pools;
import android.util.SparseArray;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventSender;
import android.view.Surface;
import com.android.internal.util.Preconditions;
@@ -67,8 +75,8 @@
mUserId = userId;
mClient = new ITvIAppClient.Stub() {
@Override
- public void onSessionCreated(String iAppServiceId, IBinder token, int seq) {
- // TODO: use InputChannel for input events
+ public void onSessionCreated(String iAppServiceId, IBinder token, InputChannel channel,
+ int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
@@ -77,7 +85,7 @@
}
Session session = null;
if (token != null) {
- session = new Session(token, mService, mUserId, seq,
+ session = new Session(token, channel, mService, mUserId, seq,
mSessionCallbackRecordMap);
} else {
mSessionCallbackRecordMap.delete(seq);
@@ -111,6 +119,18 @@
record.postLayoutSurface(left, top, right, bottom);
}
}
+
+ @Override
+ public void onBroadcastInfoRequest(BroadcastInfoRequest request, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postBroadcastInfoRequest(request);
+ }
+ }
};
ITvIAppManagerCallback managerCallback = new ITvIAppManagerCallback.Stub() {
// TODO: handle IApp service state changes
@@ -351,18 +371,33 @@
* @hide
*/
public static final class Session {
+ static final int DISPATCH_IN_PROGRESS = -1;
+ static final int DISPATCH_NOT_HANDLED = 0;
+ static final int DISPATCH_HANDLED = 1;
+
+ private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
+
private final ITvIAppManager mService;
private final int mUserId;
private final int mSeq;
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
- private IBinder mToken;
+ // For scheduling input event handling on the main thread. This also serves as a lock to
+ // protect pending input events and the input channel.
+ private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
private TvInputManager.Session mInputSession;
+ private final Pools.Pool<PendingEvent> mPendingEventPool = new Pools.SimplePool<>(20);
+ private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
- private Session(IBinder token, ITvIAppManager service, int userId, int seq,
- SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
+ private IBinder mToken;
+ private TvInputEventSender mSender;
+ private InputChannel mInputChannel;
+
+ private Session(IBinder token, InputChannel channel, ITvIAppManager service, int userId,
+ int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
mToken = token;
+ mInputChannel = channel;
mService = service;
mUserId = userId;
mSeq = seq;
@@ -428,6 +463,60 @@
}
/**
+ * Dispatches an input event to this session.
+ *
+ * @param event An {@link InputEvent} to dispatch. Cannot be {@code null}.
+ * @param token A token used to identify the input event later in the callback.
+ * @param callback A callback used to receive the dispatch result. Cannot be {@code null}.
+ * @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be
+ * {@code null}.
+ * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
+ * {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
+ * {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
+ * be invoked later.
+ * @hide
+ */
+ public int dispatchInputEvent(@NonNull InputEvent event, Object token,
+ @NonNull FinishedInputEventCallback callback, @NonNull Handler handler) {
+ Preconditions.checkNotNull(event);
+ Preconditions.checkNotNull(callback);
+ Preconditions.checkNotNull(handler);
+ synchronized (mHandler) {
+ if (mInputChannel == null) {
+ return DISPATCH_NOT_HANDLED;
+ }
+ PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ // Already running on the main thread so we can send the event immediately.
+ return sendInputEventOnMainLooperLocked(p);
+ }
+
+ // Post the event to the main thread.
+ Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ return DISPATCH_IN_PROGRESS;
+ }
+ }
+
+ /**
+ * Notifies of any broadcast info response passed in from TIS.
+ *
+ * @param response response passed in from TIS.
+ */
+ public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyBroadcastInfoResponse(mToken, response, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Releases this session.
*/
public void release() {
@@ -444,12 +533,208 @@
releaseInternal();
}
+ private void flushPendingEventsLocked() {
+ mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
+
+ final int count = mPendingEvents.size();
+ for (int i = 0; i < count; i++) {
+ int seq = mPendingEvents.keyAt(i);
+ Message msg = mHandler.obtainMessage(
+ InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ }
+ }
+
private void releaseInternal() {
mToken = null;
+ synchronized (mHandler) {
+ if (mInputChannel != null) {
+ if (mSender != null) {
+ flushPendingEventsLocked();
+ mSender.dispose();
+ mSender = null;
+ }
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
synchronized (mSessionCallbackRecordMap) {
mSessionCallbackRecordMap.delete(mSeq);
}
}
+
+ private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
+ FinishedInputEventCallback callback, Handler handler) {
+ PendingEvent p = mPendingEventPool.acquire();
+ if (p == null) {
+ p = new PendingEvent();
+ }
+ p.mEvent = event;
+ p.mEventToken = token;
+ p.mCallback = callback;
+ p.mEventHandler = handler;
+ return p;
+ }
+
+ // Assumes the event has already been removed from the queue.
+ void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
+ p.mHandled = handled;
+ if (p.mEventHandler.getLooper().isCurrentThread()) {
+ // Already running on the callback handler thread so we can send the callback
+ // immediately.
+ p.run();
+ } else {
+ // Post the event to the callback handler thread.
+ // In this case, the callback will be responsible for recycling the event.
+ Message msg = Message.obtain(p.mEventHandler, p);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ }
+ }
+
+ // Must be called on the main looper
+ private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
+ synchronized (mHandler) {
+ int result = sendInputEventOnMainLooperLocked(p);
+ if (result == DISPATCH_IN_PROGRESS) {
+ return;
+ }
+ }
+
+ invokeFinishedInputEventCallback(p, false);
+ }
+
+ private int sendInputEventOnMainLooperLocked(PendingEvent p) {
+ if (mInputChannel != null) {
+ if (mSender == null) {
+ mSender = new TvInputEventSender(mInputChannel, mHandler.getLooper());
+ }
+
+ final InputEvent event = p.mEvent;
+ final int seq = event.getSequenceNumber();
+ if (mSender.sendInputEvent(seq, event)) {
+ mPendingEvents.put(seq, p);
+ Message msg = mHandler.obtainMessage(
+ InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
+ return DISPATCH_IN_PROGRESS;
+ }
+
+ Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
+ + event);
+ }
+ return DISPATCH_NOT_HANDLED;
+ }
+
+ void finishedInputEvent(int seq, boolean handled, boolean timeout) {
+ final PendingEvent p;
+ synchronized (mHandler) {
+ int index = mPendingEvents.indexOfKey(seq);
+ if (index < 0) {
+ return; // spurious, event already finished or timed out
+ }
+
+ p = mPendingEvents.valueAt(index);
+ mPendingEvents.removeAt(index);
+
+ if (timeout) {
+ Log.w(TAG, "Timeout waiting for session to handle input event after "
+ + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
+ } else {
+ mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
+ }
+ }
+
+ invokeFinishedInputEventCallback(p, handled);
+ }
+
+ private void recyclePendingEventLocked(PendingEvent p) {
+ p.recycle();
+ mPendingEventPool.release(p);
+ }
+
+ /**
+ * Callback that is invoked when an input event that was dispatched to this session has been
+ * finished.
+ *
+ * @hide
+ */
+ public interface FinishedInputEventCallback {
+ /**
+ * Called when the dispatched input event is finished.
+ *
+ * @param token A token passed to {@link #dispatchInputEvent}.
+ * @param handled {@code true} if the dispatched input event was handled properly.
+ * {@code false} otherwise.
+ */
+ void onFinishedInputEvent(Object token, boolean handled);
+ }
+
+ private final class InputEventHandler extends Handler {
+ public static final int MSG_SEND_INPUT_EVENT = 1;
+ public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
+ public static final int MSG_FLUSH_INPUT_EVENT = 3;
+
+ InputEventHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SEND_INPUT_EVENT: {
+ sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
+ return;
+ }
+ case MSG_TIMEOUT_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, true);
+ return;
+ }
+ case MSG_FLUSH_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, false);
+ return;
+ }
+ }
+ }
+ }
+
+ private final class TvInputEventSender extends InputEventSender {
+ TvInputEventSender(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEventFinished(int seq, boolean handled) {
+ finishedInputEvent(seq, handled, false);
+ }
+ }
+
+ private final class PendingEvent implements Runnable {
+ public InputEvent mEvent;
+ public Object mEventToken;
+ public FinishedInputEventCallback mCallback;
+ public Handler mEventHandler;
+ public boolean mHandled;
+
+ public void recycle() {
+ mEvent = null;
+ mEventToken = null;
+ mCallback = null;
+ mEventHandler = null;
+ mHandled = false;
+ }
+
+ @Override
+ public void run() {
+ mCallback.onFinishedInputEvent(mEventToken, mHandled);
+
+ synchronized (mEventHandler) {
+ recyclePendingEventLocked(this);
+ }
+ }
+ }
}
private static final class SessionCallbackRecord {
@@ -490,6 +775,15 @@
}
});
}
+
+ void postBroadcastInfoRequest(final BroadcastInfoRequest request) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSession.getInputSession().requestBroadcastInfo(request);
+ }
+ });
+ }
}
/**
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index 8863729..fe087ca 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -23,12 +23,21 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.BroadcastInfoResponse;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.Surface;
import com.android.internal.os.SomeArgs;
@@ -62,21 +71,38 @@
public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
private final Handler mServiceHandler = new ServiceHandler();
+ private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks =
+ new RemoteCallbackList<>();
/** @hide */
@Override
public final IBinder onBind(Intent intent) {
ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() {
+ @Override
+ public void registerCallback(ITvIAppServiceCallback cb) {
+ if (cb != null) {
+ mCallbacks.register(cb);
+ }
+ }
@Override
- public void createSession(ITvIAppSessionCallback cb, String iAppServiceId, int type) {
+ public void unregisterCallback(ITvIAppServiceCallback cb) {
+ if (cb != null) {
+ mCallbacks.unregister(cb);
+ }
+ }
+
+ @Override
+ public void createSession(InputChannel channel, ITvIAppSessionCallback cb,
+ String iAppServiceId, int type) {
if (cb == null) {
return;
}
SomeArgs args = SomeArgs.obtain();
- args.arg1 = cb;
- args.arg2 = iAppServiceId;
- args.arg3 = type;
+ args.arg1 = channel;
+ args.arg2 = cb;
+ args.arg3 = iAppServiceId;
+ args.arg4 = type;
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args)
.sendToTarget();
}
@@ -106,6 +132,8 @@
* @hide
*/
public abstract static class Session implements KeyEvent.Callback {
+ private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
+
private final Object mLock = new Object();
// @GuardedBy("mLock")
private ITvIAppSessionCallback mSessionCallback;
@@ -137,7 +165,7 @@
* Called when the application sets the surface.
*
* <p>The TV IApp service should render interactive app UI onto the given surface. When
- * called with {@code null}, the input service should immediately free any references to the
+ * called with {@code null}, the IApp service should immediately free any references to the
* currently set surface and stop using it.
*
* @param surface The surface to be used for interactive app UI rendering. Can be
@@ -159,6 +187,14 @@
}
/**
+ * Called when a broadcast info response is received from TIS.
+ *
+ * @param response response received from TIS.
+ */
+ public void onNotifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+ }
+
+ /**
* Releases TvIAppService session.
* @hide
*/
@@ -166,6 +202,60 @@
}
/**
+ * TODO: JavaDoc of APIs related to input events.
+ * @hide
+ */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean onTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean onTrackballEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
* Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
* is relative to the overlay view that sits on top of this surface.
*
@@ -198,6 +288,26 @@
});
}
+ public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestBroadcastInfo (requestId="
+ + request.getRequestId() + ")");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onBroadcastInfoRequest(request);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestBroadcastInfo", e);
+ }
+ }
+ });
+ }
+
void startIApp() {
onStartIApp();
}
@@ -210,6 +320,39 @@
}
}
+ /**
+ * Takes care of dispatching incoming input events and tells whether the event was handled.
+ */
+ int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
+ if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
+ if (event instanceof KeyEvent) {
+ KeyEvent keyEvent = (KeyEvent) event;
+ if (keyEvent.dispatch(this, mDispatcherState, this)) {
+ return TvIAppManager.Session.DISPATCH_HANDLED;
+ }
+
+ // TODO: special handlings of navigation keys and media keys
+ } else if (event instanceof MotionEvent) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ final int source = motionEvent.getSource();
+ if (motionEvent.isTouchEvent()) {
+ if (onTouchEvent(motionEvent)) {
+ return TvIAppManager.Session.DISPATCH_HANDLED;
+ }
+ } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ if (onTrackballEvent(motionEvent)) {
+ return TvIAppManager.Session.DISPATCH_HANDLED;
+ }
+ } else {
+ if (onGenericMotionEvent(motionEvent)) {
+ return TvIAppManager.Session.DISPATCH_HANDLED;
+ }
+ }
+ }
+ // TODO: handle overlay view
+ return TvIAppManager.Session.DISPATCH_NOT_HANDLED;
+ }
+
private void initialize(ITvIAppSessionCallback callback) {
synchronized (mLock) {
mSessionCallback = callback;
@@ -243,6 +386,18 @@
onSurfaceChanged(format, width, height);
}
+ /**
+ *
+ * Calls {@link #notifyBroadcastInfoResponse}.
+ */
+ void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyBroadcastInfoResponse (requestId="
+ + response.getRequestId() + ")");
+ }
+ onNotifyBroadcastInfoResponse(response);
+ }
+
private void executeOrPostRunnableOnMainThread(Runnable action) {
synchronized (mLock) {
if (mSessionCallback == null) {
@@ -265,10 +420,17 @@
* @hide
*/
public static class ITvIAppSessionWrapper extends ITvIAppSession.Stub {
+ // TODO: put ITvIAppSessionWrapper in a separate Java file
private final Session mSessionImpl;
+ private InputChannel mChannel;
+ private TvIAppEventReceiver mReceiver;
- public ITvIAppSessionWrapper(Session mSessionImpl) {
+ public ITvIAppSessionWrapper(Context context, Session mSessionImpl, InputChannel channel) {
this.mSessionImpl = mSessionImpl;
+ mChannel = channel;
+ if (channel != null) {
+ mReceiver = new TvIAppEventReceiver(channel, context.getMainLooper());
+ }
}
@Override
@@ -290,6 +452,31 @@
public void dispatchSurfaceChanged(int format, int width, int height) {
mSessionImpl.dispatchSurfaceChanged(format, width, height);
}
+
+ @Override
+ public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+ mSessionImpl.notifyBroadcastInfoResponse(response);
+ }
+
+ private final class TvIAppEventReceiver extends InputEventReceiver {
+ TvIAppEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (mSessionImpl == null) {
+ // The session has been finished.
+ finishInputEvent(event, false);
+ return;
+ }
+
+ int handled = mSessionImpl.dispatchInputEvent(event, this);
+ if (handled != TvIAppManager.Session.DISPATCH_IN_PROGRESS) {
+ finishInputEvent(event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
+ }
+ }
+ }
}
@SuppressLint("HandlerLeak")
@@ -302,9 +489,10 @@
switch (msg.what) {
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs) msg.obj;
- ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg1;
- String iAppServiceId = (String) args.arg2;
- int type = (int) args.arg3;
+ InputChannel channel = (InputChannel) args.arg1;
+ ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg2;
+ String iAppServiceId = (String) args.arg3;
+ int type = (int) args.arg4;
args.recycle();
Session sessionImpl = onCreateSession(iAppServiceId, type);
if (sessionImpl == null) {
@@ -316,7 +504,8 @@
}
return;
}
- ITvIAppSession stub = new ITvIAppSessionWrapper(sessionImpl);
+ ITvIAppSession stub = new ITvIAppSessionWrapper(
+ TvIAppService.this, sessionImpl, channel);
SomeArgs someArgs = SomeArgs.obtain();
someArgs.arg1 = sessionImpl;
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index 8bcf3d2..ed04754 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -186,9 +186,10 @@
private final boolean mIsPassthrough;
private int mAudioStreamType = AUDIO_STREAM_TYPE_UNDEFINED;
private int mVideoStreamType = VIDEO_STREAM_TYPE_UNDEFINED;
+ private final boolean mUseSecureMemory;
- private AvSettings(int mainType, boolean isAudio, boolean isPassthrough,
- int audioStreamType, int videoStreamType) {
+ private AvSettings(int mainType, boolean isAudio, boolean isPassthrough, int audioStreamType,
+ int videoStreamType, boolean useSecureMemory) {
super(TunerUtils.getFilterSubtype(
mainType,
isAudio
@@ -197,6 +198,7 @@
mIsPassthrough = isPassthrough;
mAudioStreamType = audioStreamType;
mVideoStreamType = videoStreamType;
+ mUseSecureMemory = useSecureMemory;
}
/**
@@ -223,6 +225,16 @@
}
/**
+ * Checks whether secure memory is used.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. The return value on HAL 1.1 and
+ * lower is undefined. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ public boolean useSecureMemory() {
+ return mUseSecureMemory;
+ }
+
+ /**
* Creates a builder for {@link AvSettings}.
*
* @param mainType the filter main type.
@@ -239,9 +251,10 @@
public static class Builder {
private final int mMainType;
private final boolean mIsAudio;
- private boolean mIsPassthrough;
+ private boolean mIsPassthrough = false;
private int mAudioStreamType = AUDIO_STREAM_TYPE_UNDEFINED;
private int mVideoStreamType = VIDEO_STREAM_TYPE_UNDEFINED;
+ boolean mUseSecureMemory = false;
private Builder(int mainType, boolean isAudio) {
mMainType = mainType;
@@ -250,6 +263,8 @@
/**
* Sets whether it's passthrough.
+ *
+ * <p>Default value is {@code false}.
*/
@NonNull
public Builder setPassthrough(boolean isPassthrough) {
@@ -263,6 +278,8 @@
* <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
* no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
*
+ * <p>Default is {@link #AUDIO_STREAM_TYPE_UNDEFINED}.
+ *
* @param audioStreamType the audio stream type to set.
*/
@NonNull
@@ -281,6 +298,8 @@
* <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
* no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
*
+ * <p>Default value is {@link #VIDEO_STREAM_TYPE_UNDEFINED}.
+ *
* @param videoStreamType the video stream type to set.
*/
@NonNull
@@ -294,12 +313,29 @@
}
/**
+ * Sets whether secure memory should be used.
+ *
+ * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * <p>Default value is {@code false}.
+ */
+ @NonNull
+ public Builder setUseSecureMemory(boolean useSecureMemory) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "setSecureMemory")) {
+ mUseSecureMemory = useSecureMemory;
+ }
+ return this;
+ }
+
+ /**
* Builds a {@link AvSettings} object.
*/
@NonNull
public AvSettings build() {
- return new AvSettings(mMainType, mIsAudio, mIsPassthrough,
- mAudioStreamType, mVideoStreamType);
+ return new AvSettings(mMainType, mIsAudio, mIsPassthrough, mAudioStreamType,
+ mVideoStreamType, mUseSecureMemory);
}
}
}
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index ae271120..f0f576e 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -251,8 +251,8 @@
private native int nativeFlushFilter();
private native int nativeRead(byte[] buffer, long offset, long size);
private native int nativeClose();
- private native String nativeCreateSharedFilter();
- private native void nativeReleaseSharedFilter(String token);
+ private native String nativeAcquireSharedFilterToken();
+ private native void nativeFreeSharedFilterToken(String token);
// Called by JNI
private Filter(long id) {
@@ -562,20 +562,20 @@
}
/**
- * Creates a shared filter.
+ * Acquires a shared filter token.
*
* @return a string shared filter token.
*/
@Nullable
- public String createSharedFilter() {
+ public String acquireSharedFilterToken() {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (mIsStarted || mIsShared) {
- Log.d(TAG, "Create shared filter in a wrong state, started: " +
+ Log.d(TAG, "Acquire shared filter in a wrong state, started: " +
mIsStarted + "shared: " + mIsShared);
return null;
}
- String token = nativeCreateSharedFilter();
+ String token = nativeAcquireSharedFilterToken();
if (token != null) {
mIsShared = true;
}
@@ -584,17 +584,17 @@
}
/**
- * Releases a shared filter.
+ * Frees a shared filter token.
*
* @param filterToken the token of the shared filter being released.
*/
- public void releaseSharedFilter(@NonNull String filterToken) {
+ public void freeSharedFilterToken(@NonNull String filterToken) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (!mIsShared) {
return;
}
- nativeReleaseSharedFilter(filterToken);
+ nativeFreeSharedFilterToken(filterToken);
mIsShared = false;
}
}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index cd70365..d34581d 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -20,11 +20,11 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.DemuxRecordScIndexType;
+import android.hardware.tv.tuner.DemuxScAvcIndex;
import android.hardware.tv.tuner.DemuxScHevcIndex;
import android.hardware.tv.tuner.DemuxScIndex;
import android.hardware.tv.tuner.DemuxTsIndex;
import android.media.tv.tuner.TunerUtils;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -139,7 +139,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "INDEX_TYPE_", value =
- {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC})
+ {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC, INDEX_TYPE_SC_AVC})
public @interface ScIndexType {}
/**
@@ -154,6 +154,10 @@
* Start Code index for HEVC.
*/
public static final int INDEX_TYPE_SC_HEVC = DemuxRecordScIndexType.SC_HEVC;
+ /**
+ * Start Code index for AVC.
+ */
+ public static final int INDEX_TYPE_SC_AVC = DemuxRecordScIndexType.SC_AVC;
/**
* Indexes can be tagged by Start Code in PES (Packetized Elementary Stream)
@@ -187,23 +191,23 @@
/**
* All blocks are coded as I blocks.
*/
- public static final int SC_INDEX_I_SLICE = DemuxScIndex.I_SLICE;
+ public static final int SC_INDEX_I_SLICE = DemuxScAvcIndex.I_SLICE << 4;
/**
* Blocks are coded as I or P blocks.
*/
- public static final int SC_INDEX_P_SLICE = DemuxScIndex.P_SLICE;
+ public static final int SC_INDEX_P_SLICE = DemuxScAvcIndex.P_SLICE << 4;
/**
* Blocks are coded as I, P or B blocks.
*/
- public static final int SC_INDEX_B_SLICE = DemuxScIndex.B_SLICE;
+ public static final int SC_INDEX_B_SLICE = DemuxScAvcIndex.B_SLICE << 4;
/**
* A so-called switching I slice that is coded.
*/
- public static final int SC_INDEX_SI_SLICE = DemuxScIndex.SI_SLICE;
+ public static final int SC_INDEX_SI_SLICE = DemuxScAvcIndex.SI_SLICE << 4;
/**
* A so-called switching P slice that is coded.
*/
- public static final int SC_INDEX_SP_SLICE = DemuxScIndex.SP_SLICE;
+ public static final int SC_INDEX_SP_SLICE = DemuxScAvcIndex.SP_SLICE << 4;
/**
* Indexes can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2.
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index fc6451f..ff8f796 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.hardware.tv.tuner.Constant;
/**
* Table information for Section Filter.
@@ -26,6 +27,12 @@
*/
@SystemApi
public class SectionSettingsWithTableInfo extends SectionSettings {
+ /**
+ * The invalid version number of {@link SectionSettingsWithTableInfo}. Tuner HAL will ignore the
+ * {@link SectionSettingsWithTableInfo} version number if this invalid version is set.
+ */
+ public static final int INVALID_TABLE_INFO_VERSION = Constant.INVALID_TABINFO_VERSION;
+
private final int mTableId;
private final int mVersion;
@@ -64,7 +71,7 @@
*/
public static class Builder extends SectionSettings.Builder<Builder> {
private int mTableId;
- private int mVersion;
+ private int mVersion = INVALID_TABLE_INFO_VERSION;
private Builder(int mainType) {
super(mainType);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index c4dfee4..4c1ed45 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -693,6 +693,10 @@
sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scIndex>();
} else if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scHevc) {
sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scHevc>();
+ } else if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scAvc) {
+ sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scAvc>();
+ // Java uses the values defined by HIDL HAL. Left shift 4 bits.
+ sc = sc << 4;
}
jint ts = tsRecordEvent.tsIndexMask;
@@ -3391,8 +3395,11 @@
jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings");
bool isPassthrough =
env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z"));
- DemuxFilterAvSettings filterAvSettings {
- .isPassthrough = isPassthrough,
+ bool isSecureMemory =
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mUseSecureMemory", "Z"));
+ DemuxFilterAvSettings filterAvSettings{
+ .isPassthrough = isPassthrough,
+ .isSecureMemory = isSecureMemory,
};
return filterAvSettings;
}
@@ -3440,6 +3447,11 @@
env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexType", "I")));
jint scIndexMask = env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexMask", "I"));
+ // Backward compatibility for S- apps.
+ if (scIndexType == DemuxRecordScIndexType::SC &&
+ scIndexMask > static_cast<int32_t>(DemuxScIndex::SEQUENCE)) {
+ scIndexType = DemuxRecordScIndexType::SC_AVC;
+ }
DemuxFilterRecordSettings filterRecordSettings {
.tsIndexMask = tsIndexMask,
.scIndexType = scIndexType,
@@ -3448,6 +3460,9 @@
filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scIndex>(scIndexMask);
} else if (scIndexType == DemuxRecordScIndexType::SC_HEVC) {
filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scHevc>(scIndexMask);
+ } else if (scIndexType == DemuxRecordScIndexType::SC_AVC) {
+ // Java uses the values defined by HIDL HAL. Right shift 4 bits.
+ filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scAvc>(scIndexMask >> 4);
}
return filterRecordSettings;
}
@@ -3890,22 +3905,22 @@
return (jint)r;
}
-static jstring android_media_tv_Tuner_create_shared_filter(JNIEnv *env, jobject filter) {
+static jstring android_media_tv_Tuner_acquire_shared_filter_token(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
if (filterClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to create shared filter: filter client not found");
+ "Failed to acquire shared filter token: filter client not found");
return nullptr;
}
- string token = filterClient->createSharedFilter();
+ string token = filterClient->acquireSharedFilterToken();
if (token.empty()) {
return nullptr;
}
return env->NewStringUTF(token.data());
}
-static void android_media_tv_Tuner_release_shared_filter(
+static void android_media_tv_Tuner_free_shared_filter_token(
JNIEnv *env, jobject filter, jstring token) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
if (filterClient == nullptr) {
@@ -3915,7 +3930,7 @@
}
std::string filterToken(env->GetStringUTFChars(token, nullptr));
- filterClient->releaseSharedFilter(filterToken);
+ filterClient->freeSharedFilterToken(filterToken);
}
static sp<TimeFilterClient> getTimeFilterClient(JNIEnv *env, jobject filter) {
@@ -4408,10 +4423,10 @@
{ "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
{ "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
- {"nativeCreateSharedFilter", "()Ljava/lang/String;",
- (void *)android_media_tv_Tuner_create_shared_filter},
- {"nativeReleaseSharedFilter", "(Ljava/lang/String;)V",
- (void *)android_media_tv_Tuner_release_shared_filter},
+ {"nativeAcquireSharedFilterToken", "()Ljava/lang/String;",
+ (void *)android_media_tv_Tuner_acquire_shared_filter_token},
+ {"nativeFreeSharedFilterToken", "(Ljava/lang/String;)V",
+ (void *)android_media_tv_Tuner_free_shared_filter_token},
};
static const JNINativeMethod gSharedFilterMethods[] = {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index e8b3de8..8568383 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -196,10 +196,10 @@
return Result::INVALID_STATE;
}
-string FilterClient::createSharedFilter() {
+string FilterClient::acquireSharedFilterToken() {
if (mTunerFilter != nullptr) {
string filterToken;
- if (mTunerFilter->createSharedFilter(&filterToken).isOk()) {
+ if (mTunerFilter->acquireSharedFilterToken(&filterToken).isOk()) {
return filterToken;
}
}
@@ -207,9 +207,9 @@
return "";
}
-Result FilterClient::releaseSharedFilter(const string& filterToken) {
+Result FilterClient::freeSharedFilterToken(const string& filterToken) {
if (mTunerFilter != nullptr) {
- Status s = mTunerFilter->releaseSharedFilter(filterToken);
+ Status s = mTunerFilter->freeSharedFilterToken(filterToken);
return ClientHelper::getServiceSpecificErrorCode(s);
}
@@ -346,4 +346,13 @@
mAvSharedMemSize = 0;
mAvSharedHandle = nullptr;
}
+
+Result FilterClient::setDelayHint(const FilterDelayHint& hint) {
+ if (mTunerFilter) {
+ Status s = mTunerFilter->setDelayHint(hint);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
+ return Result::INVALID_STATE;
+}
+
} // namespace android
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index c031b2a..20e5610 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -33,6 +33,7 @@
using ::aidl::android::hardware::tv::tuner::DemuxFilterSettings;
using ::aidl::android::hardware::tv::tuner::DemuxFilterStatus;
using ::aidl::android::hardware::tv::tuner::DemuxFilterType;
+using ::aidl::android::hardware::tv::tuner::FilterDelayHint;
using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
using ::aidl::android::media::tv::tuner::ITunerFilter;
using ::android::hardware::EventFlag;
@@ -143,14 +144,19 @@
Result close();
/**
- * Create a new SharedFiler.
+ * Accquire a new SharedFiler token.
*/
- string createSharedFilter();
+ string acquireSharedFilterToken();
/**
- * Release SharedFiler.
+ * Release SharedFiler token.
*/
- Result releaseSharedFilter(const string& filterToken);
+ Result freeSharedFilterToken(const string& filterToken);
+
+ /**
+ * Set a filter delay hint.
+ */
+ Result setDelayHint(const FilterDelayHint& hint);
private:
Result getFilterMq();
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
new file mode 100644
index 0000000..2b81200
--- /dev/null
+++ b/omapi/aidl/Android.bp
@@ -0,0 +1,35 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+ name: "android.se.omapi",
+ vendor_available: true,
+ srcs: ["android/se/omapi/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ rust: {
+ enabled: true,
+ },
+ ndk: {
+ separate_platform_variant: false,
+ },
+ },
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl
new file mode 100644
index 0000000..725013a
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementChannel {
+ void close();
+ boolean isClosed();
+ boolean isBasicChannel();
+ byte[] getSelectResponse();
+ byte[] transmit(in byte[] command);
+ boolean selectNext();
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl
new file mode 100644
index 0000000..77e1c53f
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementListener {
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl
new file mode 100644
index 0000000..2b10c47
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementReader {
+ boolean isSecureElementPresent();
+ android.se.omapi.ISecureElementSession openSession();
+ void closeSessions();
+ boolean reset();
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl
new file mode 100644
index 0000000..0c8e431
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ *//*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementService {
+ String[] getReaders();
+ android.se.omapi.ISecureElementReader getReader(in String reader);
+ boolean[] isNfcEventAllowed(in String reader, in byte[] aid, in String[] packageNames, in int userId);
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl
new file mode 100644
index 0000000..06287c5
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ *//*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementSession {
+ byte[] getAtr();
+ void close();
+ void closeChannels();
+ boolean isClosed();
+ android.se.omapi.ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener);
+ android.se.omapi.ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener);
+}
diff --git a/core/java/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl
similarity index 94%
rename from core/java/android/se/omapi/ISecureElementChannel.aidl
rename to omapi/aidl/android/se/omapi/ISecureElementChannel.aidl
index 4ae57ab..bbd3c14 100644
--- a/core/java/android/se/omapi/ISecureElementChannel.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl
@@ -22,6 +22,7 @@
import android.se.omapi.ISecureElementSession;
/** @hide */
+@VintfStability
interface ISecureElementChannel {
/**
@@ -58,6 +59,9 @@
* Transmits the specified command APDU and returns the response APDU.
* MANAGE channel commands are not supported.
* Selection of applets is not supported in logical channels.
+ *
+ * @param command Command APDU, its structure is defined in ISO/IEC 7816-4
+ * in Standard byte format
*/
byte[] transmit(in byte[] command);
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl
similarity index 97%
rename from core/java/android/se/omapi/ISecureElementListener.aidl
rename to omapi/aidl/android/se/omapi/ISecureElementListener.aidl
index e9dd181..479dcd7 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl
@@ -23,5 +23,6 @@
* Interface to receive call-backs when the service is connected.
* @hide
*/
+@VintfStability
interface ISecureElementListener {
}
diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl
similarity index 94%
rename from core/java/android/se/omapi/ISecureElementReader.aidl
rename to omapi/aidl/android/se/omapi/ISecureElementReader.aidl
index 41244ab..a6979fa 100644
--- a/core/java/android/se/omapi/ISecureElementReader.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl
@@ -22,6 +22,7 @@
import android.se.omapi.ISecureElementSession;
/** @hide */
+@VintfStability
interface ISecureElementReader {
/**
@@ -34,7 +35,7 @@
* Connects to a secure element in this reader. <br>
* This method prepares (initialises) the Secure Element for communication
* before the Session object is returned (e.g. powers the Secure Element by
- * ICC ON if its not already on). There might be multiple sessions opened at
+ * ICC ON if it is not already on). There might be multiple sessions opened at
* the same time on the same reader. The system ensures the interleaving of
* APDUs between the respective sessions.
*
diff --git a/core/java/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/android/se/omapi/ISecureElementService.aidl
similarity index 66%
rename from core/java/android/se/omapi/ISecureElementService.aidl
rename to omapi/aidl/android/se/omapi/ISecureElementService.aidl
index 4fa799e..13707ec 100644
--- a/core/java/android/se/omapi/ISecureElementService.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementService.aidl
@@ -28,23 +28,31 @@
* SecureElement service interface.
* @hide
*/
+@VintfStability
interface ISecureElementService {
/**
* Returns the friendly names of available Secure Element readers.
+ * <ul>
+ * <li>If the reader is a SIM reader, then its name must be "SIM[Slot]".</li>
+ * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
+ * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
+ * </ul>
+ * Slot is a decimal number without leading zeros. The Numbering must start with 1
+ * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
*/
String[] getReaders();
/**
* Returns SecureElement Service reader object to the given name.
*/
- ISecureElementReader getReader(String reader);
+ ISecureElementReader getReader(in String reader);
/**
* Checks if the application defined by the package name is allowed to
* receive NFC transaction events for the defined AID.
*/
- boolean[] isNFCEventAllowed(String reader, in byte[] aid,
- in String[] packageNames);
+ boolean[] isNfcEventAllowed(in String reader, in byte[] aid,
+ in String[] packageNames, in int userId);
}
diff --git a/core/java/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl
similarity index 84%
rename from core/java/android/se/omapi/ISecureElementSession.aidl
rename to omapi/aidl/android/se/omapi/ISecureElementSession.aidl
index 8ea599f..129ecc4 100644
--- a/core/java/android/se/omapi/ISecureElementSession.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl
@@ -27,6 +27,7 @@
import android.se.omapi.ISecureElementListener;
/** @hide */
+@VintfStability
interface ISecureElementSession {
/**
@@ -45,7 +46,6 @@
*/
void closeChannels();
-
/**
* Tells if this session is closed.
*
@@ -59,15 +59,19 @@
* applet if aid != null.
* Logical channels cannot be opened with this connection.
* Use interface method openLogicalChannel() to open a logical channel.
+ * Listener is passed to secure element service and used to monitor whether
+ * the client application that uses OMAPI is still alive or not.
*/
ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2,
- ISecureElementListener listener);
+ in ISecureElementListener listener);
/**
* Opens a connection using the next free logical channel of the card in the
* specified reader. Selects the specified applet.
* Selection of other applets with this connection is not supported.
+ * Listener is passed to secure element service and used to monitor whether
+ * the client application that uses OMAPI is still alive or not.
*/
ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2,
- ISecureElementListener listener);
+ in ISecureElementListener listener);
}
diff --git a/omapi/aidl/vts/functional/AccessControlApp/Android.bp b/omapi/aidl/vts/functional/AccessControlApp/Android.bp
new file mode 100644
index 0000000..f03c3f6
--- /dev/null
+++ b/omapi/aidl/vts/functional/AccessControlApp/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "VtsHalOmapiSeAccessControlTestCases",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalOmapiSeAccessControlTestCases.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libnativehelper",
+ "libutils",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "VtsHalHidlTargetTestBase",
+ "android.se.omapi-V1-ndk",
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Werror",
+ ],
+ require_root: true,
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp
new file mode 100644
index 0000000..9ea6543
--- /dev/null
+++ b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+
+#include <aidl/android/se/omapi/BnSecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementChannel.h>
+#include <aidl/android/se/omapi/ISecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementReader.h>
+#include <aidl/android/se/omapi/ISecureElementService.h>
+#include <aidl/android/se/omapi/ISecureElementSession.h>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/String16.h>
+
+using namespace std;
+using namespace ::testing;
+using namespace android;
+
+int main(int argc, char** argv) {
+ InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ return status;
+}
+
+namespace {
+
+class OMAPISEAccessControlTest : public TestWithParam<std::string> {
+ protected:
+
+ class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {};
+
+ /**
+ * Verifies TLV data
+ *
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ bool verifyBerTlvData(std::vector<uint8_t> tlv) {
+ if (tlv.size() == 0) {
+ LOG(ERROR) << "Invalid tlv, null";
+ return false;
+ }
+ int i = 0;
+ if ((tlv[i++] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ i++;
+ }
+
+ int len = tlv[i++] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength; j > 0; j--) {
+ len += (len << 8) + (tlv[i++] & 0xFF);
+ }
+ }
+ // Additional 2 bytes for the SW
+ return (tlv.size() == (i + len + 2));
+ }
+
+ void testSelectableAid(
+ std::vector<std::vector<uint8_t>> authorizedAids) {
+ for (auto aid : authorizedAids) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+ }
+ }
+ }
+ }
+
+ void testUnauthorisedAid(
+ std::vector<std::vector<uint8_t>> unAuthorizedAids) {
+ for (auto aid : unAuthorizedAids) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (!res.isOk()) {
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ }
+ }
+ }
+ }
+
+ void testTransmitAPDU(
+ std::vector<uint8_t> aid,
+ std::vector<std::vector<uint8_t>> apdus) {
+ for (auto apdu : apdus) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+ bool status = false;
+ std::vector<uint8_t> selectResponse = {};
+ std::vector<uint8_t> transmitResponse = {};
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+
+ res = channel->transmit(apdu, &transmitResponse);
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ ASSERT_TRUE(res.isOk()) << "failed to transmit";
+ }
+ }
+ }
+ }
+
+ void testUnauthorisedAPDU(
+ std::vector<uint8_t> aid,
+ std::vector<std::vector<uint8_t>> apdus) {
+ for (auto apdu : apdus) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+ bool status = false;
+ std::vector<uint8_t> selectResponse = {};
+ std::vector<uint8_t> transmitResponse = {};
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+
+ res = channel->transmit(apdu, &transmitResponse);
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ if (!res.isOk()) {
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ }
+ }
+ }
+ }
+
+ bool supportOMAPIReaders() {
+ return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str()));
+ }
+
+ void getFirstApiLevel(int32_t* outApiLevel) {
+ int32_t firstApiLevel = property_get_int32(FEATURE_SE_API_LEVEL.c_str(), -1);
+ if (firstApiLevel < 0) {
+ firstApiLevel = property_get_int32(FEATURE_SE_SDK_VERSION.c_str(), -1);
+ }
+ ASSERT_GT(firstApiLevel, 0); // first_api_level must exist
+ *outApiLevel = firstApiLevel;
+ return;
+ }
+
+ bool supportsHardware() {
+ bool lowRamDevice = property_get_bool(FEATURE_SE_LOW_RAM.c_str(), true);
+ return !lowRamDevice || deviceSupportsFeature(FEATURE_SE_HARDWARE_WATCH.c_str()) ||
+ deviceSupportsFeature(FEATURE_SE_OMAPI_SERVICE.c_str()); // android.se.omapi
+ }
+
+ void SetUp() override {
+ ASSERT_TRUE(supportsHardware());
+ int32_t apiLevel;
+ getFirstApiLevel(&apiLevel);
+ ASSERT_TRUE(apiLevel > 27);
+ ASSERT_TRUE(supportOMAPIReaders());
+ LOG(INFO) << "get OMAPI service with name:" << GetParam();
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str()));
+ mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder);
+ ASSERT_TRUE(mOmapiSeService);
+
+ std::vector<std::string> readers = {};
+
+ if (mOmapiSeService != NULL) {
+ auto status = mOmapiSeService->getReaders(&readers);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ for (auto readerName : readers) {
+ // Filter eSE readers only
+ if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader;
+ status = mOmapiSeService->getReader(readerName, &reader);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ mVSReaders[readerName] = reader;
+ }
+ }
+ }
+ }
+
+ void TearDown() override {
+ if (mOmapiSeService != nullptr) {
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ reader->closeSessions();
+ }
+ }
+ }
+ }
+
+ static inline std::string const ESE_READER_PREFIX = "eSE";
+ static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+ static inline std::string const FEATURE_SE_LOW_RAM = "ro.config.low_ram";
+ static inline std::string const FEATURE_SE_HARDWARE_WATCH = "android.hardware.type.watch";
+ static inline std::string const FEATURE_SE_OMAPI_SERVICE = "com.android.se";
+ static inline std::string const FEATURE_SE_SDK_VERSION = "ro.build.version.sdk";
+ static inline std::string const FEATURE_SE_API_LEVEL = "ro.product.first_api_level";
+
+ std::vector<uint8_t> AID_40 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x40};
+ std::vector<uint8_t> AID_41 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x41};
+ std::vector<uint8_t> AID_42 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x42};
+ std::vector<uint8_t> AID_43 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x43};
+ std::vector<uint8_t> AID_44 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x44};
+ std::vector<uint8_t> AID_45 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x45};
+ std::vector<uint8_t> AID_46 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x46};
+ std::vector<uint8_t> AID_47 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x47};
+ std::vector<uint8_t> AID_48 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x48};
+ std::vector<uint8_t> AID_49 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x49};
+ std::vector<uint8_t> AID_4A = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4A};
+ std::vector<uint8_t> AID_4B = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4B};
+ std::vector<uint8_t> AID_4C = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4C};
+ std::vector<uint8_t> AID_4D = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4D};
+ std::vector<uint8_t> AID_4E = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4E};
+ std::vector<uint8_t> AID_4F = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4F};
+
+ std::vector<std::vector<uint8_t>> AUTHORIZED_AID = {AID_40, AID_41, AID_42, AID_44, AID_45,
+ AID_47, AID_48, AID_49, AID_4A, AID_4B,
+ AID_4C, AID_4D, AID_4E, AID_4F};
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_AID = {AID_43, AID_46};
+
+ /* Authorized APDU for AID_40 */
+ std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_40 = {
+ {0x00, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ };
+ /* Unauthorized APDU for AID_40 */
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_40 = {
+ {0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x06, 0x00, 0x00, 0x00},
+ };
+
+ /* Authorized APDU for AID_41 */
+ std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_41 = {
+ {0x94, 0x06, 0x00, 0x00},
+ {0x94, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}};
+ /* Unauthorized APDU for AID_41 */
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_41 = {
+ {0x00, 0x06, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ {0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x08, 0x00, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ };
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService;
+
+ std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>>
+ mVSReaders = {};
+};
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAID) {
+ testSelectableAid(AUTHORIZED_AID);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorizedAID) {
+ testUnauthorisedAid(UNAUTHORIZED_AID);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID40) {
+ testTransmitAPDU(AID_40, AUTHORIZED_APDU_AID_40);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID40) {
+ testUnauthorisedAPDU(AID_40, UNAUTHORIZED_APDU_AID_40);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID41) {
+ testTransmitAPDU(AID_41, AUTHORIZED_APDU_AID_41);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID41) {
+ testUnauthorisedAPDU(AID_41, UNAUTHORIZED_APDU_AID_41);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEAccessControlTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(
+ aidl::android::se::omapi::ISecureElementService::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEAccessControlTest);
+
+} // namespace
diff --git a/omapi/aidl/vts/functional/omapi/Android.bp b/omapi/aidl/vts/functional/omapi/Android.bp
new file mode 100644
index 0000000..c3ab8d1
--- /dev/null
+++ b/omapi/aidl/vts/functional/omapi/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "VtsHalOmapiSeServiceV1_TargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalOmapiSeServiceV1_TargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libnativehelper",
+ "libutils",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "VtsHalHidlTargetTestBase",
+ "android.se.omapi-V1-ndk",
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Werror",
+ ],
+ require_root: true,
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp
new file mode 100644
index 0000000..319cb7e
--- /dev/null
+++ b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp
@@ -0,0 +1,609 @@
+/*
+ * 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.
+ */
+
+#include <aidl/android/se/omapi/BnSecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementChannel.h>
+#include <aidl/android/se/omapi/ISecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementReader.h>
+#include <aidl/android/se/omapi/ISecureElementService.h>
+#include <aidl/android/se/omapi/ISecureElementSession.h>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/String16.h>
+
+using namespace std;
+using namespace ::testing;
+using namespace android;
+
+int main(int argc, char** argv) {
+ InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ return status;
+}
+
+namespace {
+
+class OMAPISEServiceHalTest : public TestWithParam<std::string> {
+ protected:
+ class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {};
+
+ void testSelectableAid(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> aid, std::vector<uint8_t>& selectResponse) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ }
+
+ void testNonSelectableAid(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> aid) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ LOG(ERROR) << res.getMessage();
+ ASSERT_FALSE(res.isOk()) << "expected to fail to open channel for this test";
+ }
+
+ /**
+ * Verifies TLV data
+ *
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ bool verifyBerTlvData(std::vector<uint8_t> tlv) {
+ if (tlv.size() == 0) {
+ LOG(ERROR) << "Invalid tlv, null";
+ return false;
+ }
+ int i = 0;
+ if ((tlv[i++] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ i++;
+ }
+
+ int len = tlv[i++] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength; j > 0; j--) {
+ len += (len << 8) + (tlv[i++] & 0xFF);
+ }
+ }
+ // Additional 2 bytes for the SW
+ return (tlv.size() == (i + len + 2));
+ }
+
+ void internalTransmitApdu(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+ std::vector<uint8_t> selectResponse = {};
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ res = channel->transmit(apdu, &transmitResponse);
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+ ASSERT_TRUE(res.isOk()) << "failed to transmit";
+ }
+
+ bool supportOMAPIReaders() {
+ return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str()));
+ }
+
+ void SetUp() override {
+ LOG(INFO) << "get OMAPI service with name:" << GetParam();
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str()));
+ mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder);
+ ASSERT_TRUE(mOmapiSeService);
+
+ std::vector<std::string> readers = {};
+
+ if (omapiSecureService() != NULL) {
+ auto status = omapiSecureService()->getReaders(&readers);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ for (auto readerName : readers) {
+ // Filter eSE readers only
+ if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader;
+ status = omapiSecureService()->getReader(readerName, &reader);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ mVSReaders[readerName] = reader;
+ }
+ }
+ }
+ }
+
+ void TearDown() override {
+ if (mOmapiSeService != nullptr) {
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ reader->closeSessions();
+ }
+ }
+ }
+ }
+
+ bool isDebuggableBuild() {
+ char value[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.system.build.type", value, "");
+ if (strcmp(value, "userdebug") == 0) {
+ return true;
+ }
+ if (strcmp(value, "eng") == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> omapiSecureService() {
+ return mOmapiSeService;
+ }
+
+ static inline std::string const ESE_READER_PREFIX = "eSE";
+ static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+
+ std::vector<uint8_t> SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31};
+ std::vector<uint8_t> LONG_SELECT_RESPONSE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41,
+ 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64,
+ 0x43, 0x54, 0x53, 0x32};
+ std::vector<uint8_t> NON_SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0xFF};
+
+ std::vector<std::vector<uint8_t>> ILLEGAL_COMMANDS_TRANSMIT = {
+ {0x00, 0x70, 0x00, 0x00},
+ {0x00, 0x70, 0x80, 0x00},
+ {0x00, 0xA4, 0x04, 0x04, 0x10, 0x4A, 0x53, 0x52, 0x31, 0x37, 0x37,
+ 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x30}};
+
+ /* OMAPI APDU Test case 1 and 3 */
+ std::vector<std::vector<uint8_t>> NO_DATA_APDU = {{0x00, 0x06, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ {0x94, 0x06, 0x00, 0x00},
+ {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}};
+
+ /* OMAPI APDU Test case 2 and 4 */
+ std::vector<std::vector<uint8_t>> DATA_APDU = {{0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x80, 0x08, 0x00, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}};
+
+ /* Case 2 APDU command expects the P2 received in the SELECT command as 1-byte outgoing data */
+ std::vector<uint8_t> CHECK_SELECT_P2_APDU = {0x00, 0xF4, 0x00, 0x00, 0x00};
+
+ /* OMAPI APDU Test case 1 and 3 */
+ std::vector<std::vector<uint8_t>> SW_62xx_NO_DATA_APDU = {{0x00, 0xF3, 0x00, 0x06},
+ {0x00, 0xF3, 0x00, 0x0A, 0x01, 0xAA}};
+
+ /* OMAPI APDU Test case 2 and 4 */
+ std::vector<uint8_t> SW_62xx_DATA_APDU = {0x00, 0xF3, 0x00, 0x08, 0x00};
+ std::vector<uint8_t> SW_62xx_VALIDATE_DATA_APDU = {0x00, 0xF3, 0x00, 0x0C, 0x01, 0xAA, 0x00};
+ std::vector<std::vector<uint8_t>> SW_62xx = {
+ {0x62, 0x00}, {0x62, 0x81}, {0x62, 0x82}, {0x62, 0x83}, {0x62, 0x85}, {0x62, 0xF1},
+ {0x62, 0xF2}, {0x63, 0xF1}, {0x63, 0xF2}, {0x63, 0xC2}, {0x62, 0x02}, {0x62, 0x80},
+ {0x62, 0x84}, {0x62, 0x86}, {0x63, 0x00}, {0x63, 0x81}};
+
+ std::vector<std::vector<uint8_t>> SEGMENTED_RESP_APDU = {
+ // Get response Case2 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC2, 0x08, 0x00, 0x00},
+ // Get response Case4 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC4, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00},
+ // Get response Case2 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC6, 0x08, 0x00, 0x00},
+ // Get response Case4 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC8, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00},
+ // Test device buffer capacity 7FFF data
+ {0x00, 0xC2, 0x7F, 0xFF, 0x00},
+ // Get response 6CFF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xCF, 0x08, 0x00, 0x00},
+ // Get response with another CLA with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x94, 0xC2, 0x08, 0x00, 0x00}};
+ long SERVICE_CONNECTION_TIME_OUT = 3000;
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService;
+
+ std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>>
+ mVSReaders = {};
+};
+
+/** Tests getReaders API */
+TEST_P(OMAPISEServiceHalTest, TestGetReaders) {
+ std::vector<std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> eseReaders =
+ {};
+
+ for (const auto& [name, reader] : mVSReaders) {
+ bool status = false;
+ LOG(INFO) << "Name of the reader: " << name;
+
+ if (reader) {
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ }
+ ASSERT_TRUE(status);
+
+ if (name.find(ESE_READER_PREFIX) == std::string::npos) {
+ LOG(ERROR) << "Incorrect Reader name";
+ FAIL();
+ }
+
+ if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ eseReaders.push_back(reader);
+ } else {
+ LOG(INFO) << "Reader not supported: " << name;
+ FAIL();
+ }
+ }
+
+ if (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())) {
+ ASSERT_GE(eseReaders.size(), 1);
+ } else {
+ ASSERT_TRUE(eseReaders.size() == 0);
+ }
+}
+
+/** Tests OpenBasicChannel API when aid is null */
+TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNullAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ std::vector<uint8_t> aid = {};
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ bool result = false;
+
+ auto status = reader->openSession(&session);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+ if (!session) {
+ LOG(ERROR) << "Could not open session";
+ FAIL();
+ }
+
+ status = session->openBasicChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (channel != nullptr) {
+ status = channel->isBasicChannel(&result);
+ ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened";
+ }
+ }
+ }
+}
+
+/** Tests OpenBasicChannel API when aid is provided */
+TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNonNullAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ bool result = false;
+
+ auto status = reader->openSession(&session);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+ if (!session) {
+ LOG(ERROR) << "Could not open session";
+ FAIL();
+ }
+
+ status = session->openBasicChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (channel != nullptr) {
+ status = channel->isBasicChannel(&result);
+ ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened";
+ }
+ }
+ }
+}
+
+/** Tests Select API */
+TEST_P(OMAPISEServiceHalTest, TestSelectableAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ testSelectableAid(reader, SELECTABLE_AID, selectResponse);
+ }
+ }
+}
+
+/** Tests Select API */
+TEST_P(OMAPISEServiceHalTest, TestLongSelectResponse) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ testSelectableAid(reader, LONG_SELECT_RESPONSE_AID, selectResponse);
+ ASSERT_TRUE(verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+ }
+ }
+}
+
+/** Test to fail open channel with wrong aid */
+TEST_P(OMAPISEServiceHalTest, TestWrongAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ testNonSelectableAid(reader, NON_SELECTABLE_AID);
+ }
+ }
+}
+
+/** Tests with invalid cmds in Transmit */
+TEST_P(OMAPISEServiceHalTest, TestSecurityExceptionInTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+ std::vector<uint8_t> selectResponse = {};
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+
+ for (auto cmd : ILLEGAL_COMMANDS_TRANSMIT) {
+ std::vector<uint8_t> response = {};
+ res = channel->transmit(cmd, &response);
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ }
+ }
+}
+
+/**
+ * Tests Transmit API for all readers.
+ *
+ * Checks the return status and verifies the size of the
+ * response.
+ */
+TEST_P(OMAPISEServiceHalTest, TestTransmitApdu) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : NO_DATA_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ ASSERT_GE(response.size(), 2);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ }
+
+ for (auto apdu : DATA_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ /* 256 byte data and 2 bytes of status word */
+ ASSERT_GE(response.size(), 258);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ }
+ }
+ }
+}
+
+/**
+ * Tests if underlying implementations returns the correct Status Word
+ *
+ * TO verify that :
+ * - the device does not modify the APDU sent to the Secure Element
+ * - the warning code is properly received by the application layer as SW answer
+ * - the verify that the application layer can fetch the additionnal data (when present)
+ */
+TEST_P(OMAPISEServiceHalTest, testStatusWordTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : SW_62xx_NO_DATA_APDU) {
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_GE(response.size(), 2);
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+ }
+
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ std::vector<uint8_t> apdu = SW_62xx_DATA_APDU;
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_GE(response.size(), 3);
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ std::vector<uint8_t> apdu = SW_62xx_VALIDATE_DATA_APDU;
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ ASSERT_GE(response.size(), apdu.size() + 2);
+ std::vector<uint8_t> responseSubstring((response.begin() + 0),
+ (response.begin() + apdu.size()));
+ // We should not care about which channel number is actually assigned.
+ responseSubstring[0] = apdu[0];
+ ASSERT_TRUE((responseSubstring == apdu));
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+ }
+ }
+}
+
+/** Test if the responses are segmented by the underlying implementation */
+TEST_P(OMAPISEServiceHalTest, TestSegmentedResponseTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : SEGMENTED_RESP_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ int expectedLength = (0x00 << 24) | (0x00 << 16) | (apdu[2] << 8) | apdu[3];
+ ASSERT_EQ(response.size(), (expectedLength + 2));
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ ASSERT_EQ((response[response.size() - 3] & 0xFF), (0xFF));
+ }
+ }
+ }
+}
+
+/**
+ * Tests the P2 value of the select command.
+ *
+ * Verifies that the default P2 value (0x00) is not modified by the underlying implementation.
+ */
+TEST_P(OMAPISEServiceHalTest, TestP2Value) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, CHECK_SELECT_P2_APDU, response);
+ ASSERT_GE(response.size(), 3);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ ASSERT_EQ((response[response.size() - 3] & 0xFF), (0x00));
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEServiceHalTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(
+ aidl::android::se::omapi::ISecureElementService::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEServiceHalTest);
+
+} // namespace
diff --git a/omapi/java/Android.bp b/omapi/java/Android.bp
new file mode 100644
index 0000000..8d38da0
--- /dev/null
+++ b/omapi/java/Android.bp
@@ -0,0 +1,17 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "framework-omapi-sources",
+ srcs: [
+ "**/*.java",
+ "**/*.aidl",
+ ],
+ visibility: ["//frameworks/base"],
+}
diff --git a/core/java/android/se/OWNERS b/omapi/java/android/se/OWNERS
similarity index 100%
rename from core/java/android/se/OWNERS
rename to omapi/java/android/se/OWNERS
diff --git a/core/java/android/se/omapi/Channel.java b/omapi/java/android/se/omapi/Channel.java
similarity index 100%
rename from core/java/android/se/omapi/Channel.java
rename to omapi/java/android/se/omapi/Channel.java
diff --git a/core/java/android/se/omapi/OWNERS b/omapi/java/android/se/omapi/OWNERS
similarity index 100%
rename from core/java/android/se/omapi/OWNERS
rename to omapi/java/android/se/omapi/OWNERS
diff --git a/core/java/android/se/omapi/Reader.java b/omapi/java/android/se/omapi/Reader.java
similarity index 98%
rename from core/java/android/se/omapi/Reader.java
rename to omapi/java/android/se/omapi/Reader.java
index 90c934d..3c2135d9 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/omapi/java/android/se/omapi/Reader.java
@@ -170,7 +170,9 @@
try {
closeSessions();
return mReader.reset();
- } catch (RemoteException ignore) {return false;}
+ } catch (RemoteException ignore) {
+ return false;
+ }
}
}
}
diff --git a/core/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java
similarity index 96%
rename from core/java/android/se/omapi/SEService.java
rename to omapi/java/android/se/omapi/SEService.java
index 333af91..f42ca36 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/omapi/java/android/se/omapi/SEService.java
@@ -230,20 +230,20 @@
* is not exist.
* @return A Reader object for this uicc slot.
*/
- public @NonNull Reader getUiccReader(int slotNumber) {
- if (slotNumber < 1) {
- throw new IllegalArgumentException("slotNumber should be larger than 0");
- }
- loadReaders();
+ public @NonNull Reader getUiccReader(int slotNumber) {
+ if (slotNumber < 1) {
+ throw new IllegalArgumentException("slotNumber should be larger than 0");
+ }
+ loadReaders();
- String readerName = UICC_TERMINAL + slotNumber;
- Reader reader = mReaders.get(readerName);
+ String readerName = UICC_TERMINAL + slotNumber;
+ Reader reader = mReaders.get(readerName);
- if (reader == null) {
+ if (reader == null) {
throw new IllegalArgumentException("Reader:" + readerName + " doesn't exist");
- }
+ }
- return reader;
+ return reader;
}
/**
diff --git a/core/java/android/se/omapi/Session.java b/omapi/java/android/se/omapi/Session.java
similarity index 100%
rename from core/java/android/se/omapi/Session.java
rename to omapi/java/android/se/omapi/Session.java
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/BackupEncryptionService.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/BackupEncryptionService.java
deleted file mode 100644
index 84fb0e6..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/BackupEncryptionService.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.encryption;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.encryption.transport.IntermediateEncryptingTransport;
-import com.android.server.backup.encryption.transport.IntermediateEncryptingTransportManager;
-
-/**
- * This service provides encryption of backup data. For an intent used to bind to this service, it
- * provides an {@link IntermediateEncryptingTransport} which is an implementation of {@link
- * IBackupTransport} that encrypts (or decrypts) the data when sending it (or receiving it) from the
- * real {@link IBackupTransport}.
- */
-public class BackupEncryptionService extends Service {
- public static final String TAG = "BackupEncryption";
- private static IntermediateEncryptingTransportManager sTransportManager = null;
-
- @Override
- public void onCreate() {
- Log.i(TAG, "onCreate:" + this);
- if (sTransportManager == null) {
- Log.i(TAG, "Creating IntermediateEncryptingTransportManager");
- sTransportManager = new IntermediateEncryptingTransportManager(this);
- }
- }
-
- @Override
- public void onDestroy() {
- Log.i(TAG, "onDestroy:" + this);
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- // TODO (b141536117): Check connection with TransportClient.connect and return null on fail.
- return sTransportManager.get(intent);
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- sTransportManager.cleanup(intent);
- return false;
- }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java
index 1d841b4..db2dd2f 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java
@@ -16,8 +16,6 @@
package com.android.server.backup.encryption;
-import static com.android.server.backup.encryption.BackupEncryptionService.TAG;
-
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -36,6 +34,8 @@
import java.util.Map;
public class KeyValueEncrypter {
+ private static final String TAG = "KeyValueEncrypter";
+
private final Context mContext;
private final EncryptionKeyHelper mKeyHelper;
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransport.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransport.java
deleted file mode 100644
index c3cb335..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransport.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.encryption.transport;
-
-import static com.android.server.backup.encryption.BackupEncryptionService.TAG;
-
-import android.app.backup.BackupTransport;
-import android.app.backup.RestoreDescription;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.encryption.KeyValueEncrypter;
-import com.android.server.backup.transport.DelegatingTransport;
-import com.android.server.backup.transport.TransportClient;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * This is an implementation of {@link IBackupTransport} that encrypts (or decrypts) the data when
- * sending it (or receiving it) from the {@link IBackupTransport} returned by {@link
- * TransportClient.connect(String)}.
- */
-public class IntermediateEncryptingTransport extends DelegatingTransport {
- private static final String BACKUP_TEMP_DIR = "backup";
- private static final String RESTORE_TEMP_DIR = "restore";
-
- private final TransportClient mTransportClient;
- private final Object mConnectLock = new Object();
- private final Context mContext;
- private volatile IBackupTransport mRealTransport;
- private AtomicReference<String> mNextRestorePackage = new AtomicReference<>();
- private final KeyValueEncrypter mKeyValueEncrypter;
- private final boolean mShouldEncrypt;
-
- IntermediateEncryptingTransport(
- TransportClient transportClient, Context context, boolean shouldEncrypt) {
- this(transportClient, context, new KeyValueEncrypter(context), shouldEncrypt);
- }
-
- @VisibleForTesting
- IntermediateEncryptingTransport(
- TransportClient transportClient, Context context, KeyValueEncrypter keyValueEncrypter,
- boolean shouldEncrypt) {
- mTransportClient = transportClient;
- mContext = context;
- mKeyValueEncrypter = keyValueEncrypter;
- mShouldEncrypt = shouldEncrypt;
- }
-
- @Override
- protected IBackupTransport getDelegate() throws RemoteException {
- if (mRealTransport == null) {
- connect();
- }
- Log.d(TAG, "real transport = " + mRealTransport.name());
- return mRealTransport;
- }
-
- @Override
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
- throws RemoteException {
- if (!mShouldEncrypt) {
- return super.performBackup(packageInfo, inFd, flags);
- }
-
- File encryptedStorageFile = getBackupTempStorage(packageInfo.packageName);
- if (encryptedStorageFile == null) {
- return BackupTransport.TRANSPORT_ERROR;
- }
-
- // Encrypt the backup data and write it into a temp file.
- try (OutputStream encryptedOutput = new FileOutputStream(encryptedStorageFile)) {
- mKeyValueEncrypter.encryptKeyValueData(packageInfo.packageName, inFd,
- encryptedOutput);
- } catch (Throwable e) {
- Log.e(TAG, "Failed to encrypt backup data: ", e);
- return BackupTransport.TRANSPORT_ERROR;
- }
-
- // Pass the temp file to the real transport for backup.
- try (FileInputStream encryptedInput = new FileInputStream(encryptedStorageFile)) {
- return super.performBackup(
- packageInfo, ParcelFileDescriptor.dup(encryptedInput.getFD()), flags);
- } catch (IOException e) {
- Log.e(TAG, "Failed to read encrypted data from temp storage: ", e);
- return BackupTransport.TRANSPORT_ERROR;
- }
- }
-
- @Override
- public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
- if (!mShouldEncrypt) {
- return super.getRestoreData(outFd);
- }
-
- String nextRestorePackage = mNextRestorePackage.get();
- if (nextRestorePackage == null) {
- Log.e(TAG, "No next restore package set");
- return BackupTransport.TRANSPORT_ERROR;
- }
-
- File encryptedStorageFile = getRestoreTempStorage(nextRestorePackage);
- if (encryptedStorageFile == null) {
- return BackupTransport.TRANSPORT_ERROR;
- }
-
- // Get encrypted restore data from the real transport and write it into a temp file.
- try (FileOutputStream outputStream = new FileOutputStream(encryptedStorageFile)) {
- int status = super.getRestoreData(ParcelFileDescriptor.dup(outputStream.getFD()));
- if (status != BackupTransport.TRANSPORT_OK) {
- Log.e(TAG, "Failed to read restore data from transport, status = " + status);
- return status;
- }
- } catch (IOException e) {
- Log.e(TAG, "Failed to write encrypted data to temp storage: ", e);
- return BackupTransport.TRANSPORT_ERROR;
- }
-
- // Decrypt the data and write it into the fd given by the real transport.
- try (InputStream inputStream = new FileInputStream(encryptedStorageFile)) {
- mKeyValueEncrypter.decryptKeyValueData(nextRestorePackage, inputStream, outFd);
- encryptedStorageFile.delete();
- } catch (Exception e) {
- Log.e(TAG, "Failed to decrypt restored data: ", e);
- return BackupTransport.TRANSPORT_ERROR;
- }
-
- return BackupTransport.TRANSPORT_OK;
- }
-
- @Override
- public RestoreDescription nextRestorePackage() throws RemoteException {
- if (!mShouldEncrypt) {
- return super.nextRestorePackage();
- }
-
- RestoreDescription restoreDescription = super.nextRestorePackage();
- mNextRestorePackage.set(restoreDescription.getPackageName());
-
- return restoreDescription;
- }
-
- @VisibleForTesting
- protected File getBackupTempStorage(String packageName) {
- return getTempStorage(packageName, BACKUP_TEMP_DIR);
- }
-
- @VisibleForTesting
- protected File getRestoreTempStorage(String packageName) {
- return getTempStorage(packageName, RESTORE_TEMP_DIR);
- }
-
- private File getTempStorage(String packageName, String operationType) {
- File encryptedDir = new File(mContext.getFilesDir(), operationType);
- encryptedDir.mkdir();
- File encryptedFile = new File(encryptedDir, packageName);
- try {
- encryptedFile.createNewFile();
- } catch (IOException e) {
- Log.e(TAG, "Failed to create temp file for encrypted data: ", e);
- }
- return encryptedFile;
- }
-
- private void connect() throws RemoteException {
- Log.i(TAG, "connecting " + mTransportClient);
- synchronized (mConnectLock) {
- if (mRealTransport == null) {
- mRealTransport = mTransportClient.connect("IntermediateEncryptingTransport");
- if (mRealTransport == null) {
- throw new RemoteException("Could not connect: " + mTransportClient);
- }
- }
- }
- }
-
- @VisibleForTesting
- TransportClient getClient() {
- return mTransportClient;
- }
-
- @VisibleForTesting
- void setNextRestorePackage(String nextRestorePackage) {
- mNextRestorePackage.set(nextRestorePackage);
- }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManager.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManager.java
deleted file mode 100644
index 7c4082c..0000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManager.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.encryption.transport;
-
-import static com.android.server.backup.encryption.BackupEncryptionService.TAG;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.backup.IBackupTransport;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.server.backup.transport.TransportClientManager;
-import com.android.server.backup.transport.TransportStats;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/** Handles creation and cleanup of {@link IntermediateEncryptingTransport} instances. */
-public class IntermediateEncryptingTransportManager {
- private static final String CALLER = "IntermediateEncryptingTransportManager";
- private final TransportClientManager mTransportClientManager;
- private final Object mTransportsLock = new Object();
- private final Map<ComponentName, IntermediateEncryptingTransport> mTransports = new HashMap<>();
- private Context mContext;
-
- @VisibleForTesting
- IntermediateEncryptingTransportManager(TransportClientManager transportClientManager) {
- mTransportClientManager = transportClientManager;
- }
-
- public IntermediateEncryptingTransportManager(Context context) {
- this(new TransportClientManager(UserHandle.myUserId(), context, new TransportStats()));
- mContext = context;
- }
-
- /**
- * Extract the {@link ComponentName} corresponding to the real {@link IBackupTransport}, and
- * provide a {@link IntermediateEncryptingTransport} which is an implementation of {@link
- * IBackupTransport} that encrypts (or decrypts) the data when sending it (or receiving it) from
- * the real {@link IBackupTransport}.
- *
- * @param intent {@link Intent} created with a call to {@link
- * TransportClientManager.getEncryptingTransportIntent(ComponentName)}.
- * @return
- */
- public IntermediateEncryptingTransport get(Intent intent) {
- Intent transportIntent = TransportClientManager.getRealTransportIntent(intent);
- Log.i(TAG, "get: intent:" + intent + " transportIntent:" + transportIntent);
- synchronized (mTransportsLock) {
- return mTransports.computeIfAbsent(
- transportIntent.getComponent(), c -> create(transportIntent));
- }
- }
-
- /** Create an instance of {@link IntermediateEncryptingTransport}. */
- private IntermediateEncryptingTransport create(Intent realTransportIntent) {
- Log.d(TAG, "create: intent:" + realTransportIntent);
-
- LockPatternUtils patternUtils = new LockPatternUtils(mContext);
- boolean shouldEncrypt =
- realTransportIntent.getComponent().getClassName().contains("EncryptedLocalTransport")
- && (patternUtils.isLockPatternEnabled(UserHandle.myUserId())
- || patternUtils.isLockPasswordEnabled(UserHandle.myUserId()));
-
- return new IntermediateEncryptingTransport(
- mTransportClientManager.getTransportClient(
- realTransportIntent.getComponent(),
- realTransportIntent.getExtras(),
- CALLER),
- mContext,
- shouldEncrypt);
- }
-
- /**
- * Cleanup the {@link IntermediateEncryptingTransport} which was created by a call to {@link
- * #get(Intent)} with this {@link Intent}.
- */
- public void cleanup(Intent intent) {
- Intent transportIntent = TransportClientManager.getRealTransportIntent(intent);
- Log.i(TAG, "cleanup: intent:" + intent + " transportIntent:" + transportIntent);
-
- IntermediateEncryptingTransport transport;
- synchronized (mTransportsLock) {
- transport = mTransports.remove(transportIntent.getComponent());
- }
- if (transport != null) {
- mTransportClientManager.disposeOfTransportClient(transport.getClient(), CALLER);
- } else {
- Log.i(TAG, "Could not find IntermediateEncryptingTransport");
- }
- }
-}
diff --git a/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManagerTest.java b/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManagerTest.java
deleted file mode 100644
index 0d43a19..0000000
--- a/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManagerTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.encryption.transport;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotSame;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Bundle;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportClientManager;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class IntermediateEncryptingTransportManagerTest {
- @Mock private TransportClient mTransportClient;
- @Mock private TransportClientManager mTransportClientManager;
-
- private final ComponentName mTransportComponent = new ComponentName("pkg", "class");
- private final Bundle mExtras = new Bundle();
- private Intent mEncryptingTransportIntent;
- private IntermediateEncryptingTransportManager mIntermediateEncryptingTransportManager;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mExtras.putInt("test", 1);
- mEncryptingTransportIntent =
- TransportClientManager.getEncryptingTransportIntent(mTransportComponent)
- .putExtras(mExtras);
- mIntermediateEncryptingTransportManager =
- new IntermediateEncryptingTransportManager(mTransportClientManager);
- }
-
- @Test
- public void testGet_createsClientWithRealTransportComponentAndExtras() {
- when(mTransportClientManager.getTransportClient(any(), any(), any()))
- .thenReturn(mTransportClient);
-
- IntermediateEncryptingTransport intermediateEncryptingTransport =
- mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
-
- assertEquals(mTransportClient, intermediateEncryptingTransport.getClient());
- verify(mTransportClientManager, times(1))
- .getTransportClient(eq(mTransportComponent), argThat(mExtras::kindofEquals), any());
- verifyNoMoreInteractions(mTransportClientManager);
- }
-
- @Test
- public void testGet_callTwice_returnsSameTransport() {
- IntermediateEncryptingTransport transport1 =
- mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
- IntermediateEncryptingTransport transport2 =
- mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
-
- assertEquals(transport1, transport2);
- }
-
- @Test
- public void testCleanup_disposesTransportClient() {
- when(mTransportClientManager.getTransportClient(any(), any(), any()))
- .thenReturn(mTransportClient);
-
- IntermediateEncryptingTransport transport =
- mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
- mIntermediateEncryptingTransportManager.cleanup(mEncryptingTransportIntent);
-
- verify(mTransportClientManager, times(1)).getTransportClient(any(), any(), any());
- verify(mTransportClientManager, times(1))
- .disposeOfTransportClient(eq(mTransportClient), any());
- verifyNoMoreInteractions(mTransportClientManager);
- }
-
- @Test
- public void testCleanup_removesCachedTransport() {
- when(mTransportClientManager.getTransportClient(any(), any(), any()))
- .thenReturn(mTransportClient);
-
- IntermediateEncryptingTransport transport1 =
- mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
- mIntermediateEncryptingTransportManager.cleanup(mEncryptingTransportIntent);
- IntermediateEncryptingTransport transport2 =
- mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
-
- assertNotSame(transport1, transport2);
- }
-}
diff --git a/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportTest.java b/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportTest.java
deleted file mode 100644
index a85b2e4..0000000
--- a/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.encryption.transport;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.backup.BackupTransport;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.os.ParcelFileDescriptor;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.encryption.KeyValueEncrypter;
-import com.android.server.backup.transport.TransportClient;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class IntermediateEncryptingTransportTest {
- private static final String TEST_PACKAGE_NAME = "test_package";
-
- private IntermediateEncryptingTransport mIntermediateEncryptingTransport;
- private final PackageInfo mTestPackage = new PackageInfo();
-
- @Mock private IBackupTransport mRealTransport;
- @Mock private TransportClient mTransportClient;
- @Mock private ParcelFileDescriptor mParcelFileDescriptor;
- @Mock private KeyValueEncrypter mKeyValueEncrypter;
- @Mock private Context mContext;
-
- @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
- private File mTempFile;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mIntermediateEncryptingTransport =
- new IntermediateEncryptingTransport(
- mTransportClient, mContext, mKeyValueEncrypter, true);
- mTestPackage.packageName = TEST_PACKAGE_NAME;
- mTempFile = mTemporaryFolder.newFile();
-
- when(mTransportClient.connect(anyString())).thenReturn(mRealTransport);
- when(mRealTransport.getRestoreData(any())).thenReturn(BackupTransport.TRANSPORT_OK);
- }
-
- @Test
- public void testGetDelegate_callsConnect() throws Exception {
- IBackupTransport ret = mIntermediateEncryptingTransport.getDelegate();
-
- assertEquals(mRealTransport, ret);
- verify(mTransportClient, times(1)).connect(anyString());
- verifyNoMoreInteractions(mTransportClient);
- }
-
- @Test
- public void testGetDelegate_callTwice_callsConnectOnce() throws Exception {
- when(mTransportClient.connect(anyString())).thenReturn(mRealTransport);
-
- IBackupTransport ret1 = mIntermediateEncryptingTransport.getDelegate();
- IBackupTransport ret2 = mIntermediateEncryptingTransport.getDelegate();
-
- assertEquals(mRealTransport, ret1);
- assertEquals(mRealTransport, ret2);
- verify(mTransportClient, times(1)).connect(anyString());
- verifyNoMoreInteractions(mTransportClient);
- }
-
- @Test
- public void testPerformBackup_shouldEncryptTrue_encryptsDataAndPassesToDelegate()
- throws Exception {
- mIntermediateEncryptingTransport =
- new TestIntermediateTransport(mTransportClient, mContext, mKeyValueEncrypter, true);
-
- mIntermediateEncryptingTransport.performBackup(mTestPackage, mParcelFileDescriptor, 0);
-
- verify(mKeyValueEncrypter, times(1))
- .encryptKeyValueData(eq(TEST_PACKAGE_NAME), eq(mParcelFileDescriptor), any());
- verify(mRealTransport, times(1)).performBackup(eq(mTestPackage), any(), eq(0));
- }
-
- @Test
- public void testPerformBackup_shouldEncryptFalse_doesntEncryptDataAndPassedToDelegate()
- throws Exception {
- mIntermediateEncryptingTransport =
- new TestIntermediateTransport(
- mTransportClient, mContext, mKeyValueEncrypter, false);
-
- mIntermediateEncryptingTransport.performBackup(mTestPackage, mParcelFileDescriptor, 0);
-
- verifyZeroInteractions(mKeyValueEncrypter);
- verify(mRealTransport, times(1))
- .performBackup(eq(mTestPackage), eq(mParcelFileDescriptor), eq(0));
- }
-
- @Test
- public void testGetRestoreData_shouldEncryptTrue_decryptsDataAndPassesToDelegate()
- throws Exception {
- mIntermediateEncryptingTransport =
- new TestIntermediateTransport(mTransportClient, mContext, mKeyValueEncrypter, true);
- mIntermediateEncryptingTransport.setNextRestorePackage(TEST_PACKAGE_NAME);
-
- mIntermediateEncryptingTransport.getRestoreData(mParcelFileDescriptor);
-
- verify(mKeyValueEncrypter, times(1))
- .decryptKeyValueData(eq(TEST_PACKAGE_NAME), any(), eq(mParcelFileDescriptor));
- verify(mRealTransport, times(1)).getRestoreData(any());
- }
-
- @Test
- public void testGetRestoreData_shouldEncryptFalse_doesntDecryptDataAndPassesToDelegate()
- throws Exception {
- mIntermediateEncryptingTransport =
- new TestIntermediateTransport(
- mTransportClient, mContext, mKeyValueEncrypter, false);
- mIntermediateEncryptingTransport.setNextRestorePackage(TEST_PACKAGE_NAME);
-
- mIntermediateEncryptingTransport.getRestoreData(mParcelFileDescriptor);
-
- verifyZeroInteractions(mKeyValueEncrypter);
- verify(mRealTransport, times(1)).getRestoreData(eq(mParcelFileDescriptor));
- }
-
- private final class TestIntermediateTransport extends IntermediateEncryptingTransport {
- TestIntermediateTransport(
- TransportClient transportClient,
- Context context,
- KeyValueEncrypter keyValueEncrypter,
- boolean shouldEncrypt) {
- super(transportClient, context, keyValueEncrypter, shouldEncrypt);
- }
-
- @Override
- protected File getBackupTempStorage(String packageName) {
- return mTempFile;
- }
-
- @Override
- protected File getRestoreTempStorage(String packageName) {
- return mTempFile;
- }
- }
-}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index 9f07317..126b823 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -42,8 +42,8 @@
import android.companion.BluetoothDeviceFilter;
import android.companion.BluetoothLeDeviceFilter;
import android.companion.DeviceFilter;
+import android.companion.IAssociationRequestCallback;
import android.companion.ICompanionDeviceDiscoveryService;
-import android.companion.IFindDeviceCallback;
import android.companion.WifiDeviceFilter;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -92,7 +92,7 @@
AssociationRequest mRequest;
List<DeviceFilterPair> mDevicesFound;
DeviceFilterPair mSelectedDevice;
- IFindDeviceCallback mFindCallback;
+ IAssociationRequestCallback mApplicationCallback;
AndroidFuture<String> mServiceCallback;
boolean mIsScanning = false;
@@ -104,13 +104,13 @@
@Override
public void startDiscovery(AssociationRequest request,
String callingPackage,
- IFindDeviceCallback findCallback,
+ IAssociationRequestCallback appCallback,
AndroidFuture<String> serviceCallback) {
Log.i(LOG_TAG,
"startDiscovery() called with: filter = [" + request
- + "], findCallback = [" + findCallback + "]"
+ + "], appCallback = [" + appCallback + "]"
+ "], serviceCallback = [" + serviceCallback + "]");
- mFindCallback = findCallback;
+ mApplicationCallback = appCallback;
mServiceCallback = serviceCallback;
Handler.getMain().sendMessage(obtainMessage(
CompanionDeviceDiscoveryService::startDiscovery,
@@ -299,7 +299,7 @@
//TODO also, on timeout -> call onFailure
private void onReadyToShowUI() {
try {
- mFindCallback.onSuccess(PendingIntent.getActivity(
+ mApplicationCallback.onAssociationPending(PendingIntent.getActivity(
this, 0,
new Intent(this, CompanionDeviceActivity.class),
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 801b490..a3eb0ecc 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -251,7 +251,7 @@
if (volume.getType() == VolumeInfo.TYPE_PUBLIC) {
root.flags |= Root.FLAG_HAS_SETTINGS;
}
- if (volume.isVisibleForRead(userId)) {
+ if (volume.isVisibleForUser(userId)) {
root.visiblePath = volume.getPathForUser(userId);
} else {
root.visiblePath = null;
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index 806734f..33543fd 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -80,9 +80,9 @@
<string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"Wearలో ఇన్స్టాల్/అన్ఇన్స్టాల్ చర్యలకు మద్దతు లేదు."</string>
<string name="message_staging" msgid="8032722385658438567">"యాప్ను సిద్ధం చేస్తుంది…"</string>
<string name="app_name_unknown" msgid="6881210203354323926">"తెలియదు"</string>
- <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టాబ్లెట్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
- <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టీవీ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
- <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ ఫోన్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
+ <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"మీ సెక్యూరిటీ దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టాబ్లెట్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
+ <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"మీ సెక్యూరిటీ దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టీవీ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
+ <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"మీ సెక్యూరిటీ దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ ఫోన్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్లలో మార్చవచ్చు."</string>
<string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"తెలియని యాప్లు మీ ఫోన్ పైన, వ్యక్తిగత డేటా పైన దాడి చేయడానికి ఎక్కువగా అవకాశం ఉంటుంది. ఈ యాప్ను ఇన్స్టాల్ చేయడం ద్వారా, దాని వినియోగంతో మీ ఫోన్కు ఏదైనా నష్టం జరిగితే లేదా మీ డేటాను కోల్పోతే అందుకు మీరే బాధ్యత వహిస్తారని అంగీకరిస్తున్నారు."</string>
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"మీ టాబ్లెట్ మరియు వ్యక్తిగత డేటాపై తెలియని యాప్లు దాడి చేయడానికి ఎక్కువ అవకాశం ఉంది. మీరు ఈ యాప్ను ఇన్స్టాల్ చేయడం ద్వారా, దీనిని ఉపయోగించడం వలన మీ టాబ్లెట్కు ఏదైనా హాని జరిగినా లేదా డేటా కోల్పోయినా బాధ్యత మీదేనని అంగీకరిస్తున్నారు."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"మీ టీవీ మరియు వ్యక్తిగత డేటాపై తెలియని యాప్లు దాడి చేయడానికి ఎక్కువ అవకాశం ఉంది. మీరు ఈ యాప్ను ఇన్స్టాల్ చేయడం ద్వారా, దీనిని ఉపయోగించడం వలన మీ టీవీకి ఏదైనా హాని జరిగినా లేదా డేటా కోల్పోయినా బాధ్యత మీదేనని అంగీకరిస్తున్నారు."</string>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index d898b1e..40b621c 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -56,8 +56,8 @@
<string name="print_select_printer" msgid="7388760939873368698">"Seleziona stampante"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"Elimina stampante"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> stampanti trovate</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> stampante trovata</item>
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Ulteriori informazioni su questa stampante"</string>
@@ -76,8 +76,8 @@
<string name="disabled_services_title" msgid="7313253167968363211">"Servizi disattivati"</string>
<string name="all_services_title" msgid="5578662754874906455">"Tutti i servizi"</string>
<plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
<item quantity="other">Installa per rilevare <xliff:g id="COUNT_1">%1$s</xliff:g> stampanti</item>
- <item quantity="one">Installa per rilevare <xliff:g id="COUNT_0">%1$s</xliff:g> stampante</item>
</plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Stampa di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annullamento di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 4517efe..56001d8 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -56,8 +56,8 @@
<string name="print_select_printer" msgid="7388760939873368698">"Selecionar impressora"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"Esquecer impressora"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
- <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> impressora encontrada</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Mais informações acerca desta impressora"</string>
@@ -76,8 +76,8 @@
<string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
<string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
<plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
- <item quantity="other">Instale para detetar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
<item quantity="one">Instale para detetar <xliff:g id="COUNT_0">%1$s</xliff:g> impressora</item>
+ <item quantity="other">Instale para detetar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
</plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"A imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"A cancelar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
index b8ad321..0b436a9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothUuid;
@@ -118,8 +119,8 @@
return true;
}
} else if (btClass != null) {
- if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP) ||
- btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
+ if (doesClassMatch(btClass, BluetoothClass.PROFILE_A2DP)
+ || doesClassMatch(btClass, BluetoothClass.PROFILE_HEADSET)) {
return true;
}
}
@@ -137,7 +138,7 @@
}
}
return btClass != null
- && btClass.doesClassMatch(BluetoothClass.PROFILE_OPP);
+ && doesClassMatch(btClass, BluetoothClass.PROFILE_OPP);
}
}
@@ -151,7 +152,7 @@
}
}
return btClass != null
- && btClass.doesClassMatch(BluetoothClass.PROFILE_PANU);
+ && doesClassMatch(btClass, BluetoothClass.PROFILE_PANU);
}
}
@@ -165,7 +166,12 @@
}
}
return btClass != null
- && btClass.doesClassMatch(BluetoothClass.PROFILE_NAP);
+ && doesClassMatch(btClass, BluetoothClass.PROFILE_NAP);
}
}
+
+ @SuppressLint("NewApi") // Hidden API made public
+ private static boolean doesClassMatch(BluetoothClass btClass, int classId) {
+ return btClass.doesClassMatch(classId);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 58d2185..389892e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -160,10 +160,12 @@
private void registerIntentReceiver(BroadcastReceiver receiver, IntentFilter filter) {
if (mUserHandle == null) {
// If userHandle has not been provided, simply call registerReceiver.
- mContext.registerReceiver(receiver, filter, null, mReceiverHandler);
+ mContext.registerReceiver(receiver, filter, null, mReceiverHandler,
+ Context.RECEIVER_EXPORTED);
} else {
// userHandle was explicitly specified, so need to call multi-user aware API.
- mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler);
+ mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler,
+ Context.RECEIVER_EXPORTED);
}
}
@@ -305,7 +307,8 @@
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
if (cachedDevice == null) {
cachedDevice = mDeviceManager.addDevice(device);
- Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice");
+ Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice "
+ + cachedDevice.getDevice().getAnonymizedAddress());
} else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
&& !cachedDevice.getDevice().isConnected()) {
// Dispatch device add callback to show bonded but
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 253629c..c9af4d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -2,6 +2,7 @@
import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -70,6 +71,12 @@
void onShowError(Context context, String name, int messageResId);
}
+ /**
+ * @param context to access resources from
+ * @param cachedDevice to get class from
+ * @return pair containing the drawable and the description of the Bluetooth class
+ * of the device.
+ */
public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
CachedBluetoothDevice cachedDevice) {
BluetoothClass btClass = cachedDevice.getBtClass();
@@ -110,13 +117,13 @@
}
}
if (btClass != null) {
- if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
+ if (doesClassMatch(btClass, BluetoothClass.PROFILE_HEADSET)) {
return new Pair<>(
getBluetoothDrawable(context,
com.android.internal.R.drawable.ic_bt_headset_hfp),
context.getString(R.string.bluetooth_talkback_headset));
}
- if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+ if (doesClassMatch(btClass, BluetoothClass.PROFILE_A2DP)) {
return new Pair<>(
getBluetoothDrawable(context,
com.android.internal.R.drawable.ic_bt_headphones_a2dp),
@@ -376,4 +383,9 @@
}
return Uri.parse(data);
}
+
+ @SuppressLint("NewApi") // Hidden API made public
+ private static boolean doesClassMatch(BluetoothClass btClass, int classId) {
+ return btClass.doesClassMatch(classId);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 912b468..a901160 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -194,7 +194,7 @@
void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
if (BluetoothUtils.D) {
Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device "
- + mDevice.getAlias() + ", newProfileState " + newProfileState);
+ + mDevice.getAnonymizedAddress() + ", newProfileState " + newProfileState);
}
if (mLocalAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF)
{
@@ -745,7 +745,7 @@
}
if (BluetoothUtils.D) {
- Log.d(TAG, "updating profiles for " + mDevice.getAlias());
+ Log.d(TAG, "updating profiles for " + mDevice.getAnonymizedAddress());
BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 80b03a4..e7a6b32 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -19,11 +19,13 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.le.BluetoothLeScanner;
import android.content.Context;
import android.os.ParcelUuid;
import android.util.Log;
+import java.time.Duration;
import java.util.List;
import java.util.Set;
@@ -140,7 +142,7 @@
}
public void setDiscoverableTimeout(int timeout) {
- mAdapter.setDiscoverableTimeout(timeout);
+ mAdapter.setDiscoverableTimeout(Duration.ofSeconds(timeout));
}
public long getDiscoveryEndMillis() {
@@ -156,7 +158,9 @@
}
public boolean setScanMode(int mode, int duration) {
- return mAdapter.setScanMode(mode, duration);
+ return (mAdapter.setDiscoverableTimeout(Duration.ofSeconds(duration))
+ == BluetoothStatusCodes.SUCCESS
+ && mAdapter.setScanMode(mode) == BluetoothStatusCodes.SUCCESS);
}
public void startScanning(boolean force) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 9d4669a..cd5c78d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -62,7 +62,7 @@
if (!(drawable instanceof BitmapDrawable)) {
setColorFilter(drawable);
}
- return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
+ return drawable;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 949b245..c34f65c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -29,7 +29,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
-import com.android.settingslib.bluetooth.BluetoothUtils;
import java.util.List;
@@ -61,7 +60,7 @@
public Drawable getIcon() {
final Drawable drawable = getIconWithoutBackground();
setColorFilter(drawable);
- return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
+ return drawable;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index b6c0b30..1139d33 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -32,7 +32,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
-import com.android.settingslib.bluetooth.BluetoothUtils;
/**
* PhoneMediaDevice extends MediaDevice to represents Phone device.
@@ -87,7 +86,7 @@
public Drawable getIcon() {
final Drawable drawable = getIconWithoutBackground();
setColorFilter(drawable);
- return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
+ return drawable;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index a210e90..8b17be1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -19,7 +19,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
-import android.app.Dialog;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -85,6 +84,7 @@
@VisibleForTesting
protected Context mContext;
+ private final int mThemeResId;
@VisibleForTesting
protected TextView mZenAlarmWarning;
@VisibleForTesting
@@ -97,10 +97,15 @@
protected LayoutInflater mLayoutInflater;
public EnableZenModeDialog(Context context) {
- mContext = context;
+ this(context, 0);
}
- public Dialog createDialog() {
+ public EnableZenModeDialog(Context context, int themeResId) {
+ mContext = context;
+ mThemeResId = themeResId;
+ }
+
+ public AlertDialog createDialog() {
mNotificationManager = (NotificationManager) mContext.
getSystemService(Context.NOTIFICATION_SERVICE);
mForeverId = Condition.newId(mContext).appendPath("forever").build();
@@ -108,7 +113,7 @@
mUserId = mContext.getUserId();
mAttached = false;
- final AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mContext, mThemeResId)
.setTitle(R.string.zen_mode_settings_turn_on_dialog_title)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.zen_mode_enable_dialog_turn_on,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 0fe4efe..71accc4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -47,6 +47,7 @@
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
Settings.System.ADAPTIVE_SLEEP, // moved to secure
+ Settings.System.APPLY_RAMPING_RINGER,
Settings.System.VIBRATE_INPUT_DEVICES,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
Settings.System.TEXT_AUTO_REPLACE,
@@ -80,6 +81,7 @@
Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Settings.System.RING_VIBRATION_INTENSITY,
Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
Settings.System.DISPLAY_COLOR_MODE,
Settings.System.ALARM_ALERT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 462c3a5..84e9d28 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -117,9 +117,11 @@
VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.APPLY_RAMPING_RINGER, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+ VALIDATORS.put(System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.RINGTONE, URI_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_SOUND, URI_VALIDATOR);
VALIDATORS.put(System.ALARM_ALERT, URI_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 8c669d2..86ee3b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -98,6 +98,10 @@
private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
+ // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
+ // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
+ // restoring this data.
+ private static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
@@ -253,7 +257,7 @@
deviceSpecificInformation, data);
stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
- KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
+ KEY_SIM_SPECIFIC_SETTINGS_2, simSpecificSettingsData, data);
writeNewChecksums(stateChecksums, newState);
}
@@ -284,10 +288,9 @@
// versionCode of com.android.providers.settings corresponds to SDK_INT
mRestoredFromSdkInt = (int) appVersionCode;
- HashSet<String> movedToGlobal = new HashSet<String>();
- Settings.System.getMovedToGlobalSettings(movedToGlobal);
- Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
+ Set<String> movedToGlobal = getMovedToGlobalSettings();
Set<String> movedToSecure = getMovedToSecureSettings();
+ Set<String> movedToSystem = getMovedToSystemSettings();
Set<String> preservedGlobalSettings = getSettingsToPreserveInRestore(
Settings.Global.CONTENT_URI);
@@ -318,32 +321,23 @@
switch (key) {
case KEY_SYSTEM :
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
- movedToSecure, R.array.restore_blocked_system_settings,
- dynamicBlockList,
+ movedToSecure, /* movedToSystem= */ null,
+ R.array.restore_blocked_system_settings, dynamicBlockList,
preservedSystemSettings);
mSettingsHelper.applyAudioSettings();
break;
case KEY_SECURE :
- restoreSettings(
- data,
- Settings.Secure.CONTENT_URI,
- movedToGlobal,
- null,
- R.array.restore_blocked_secure_settings,
- dynamicBlockList,
+ restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal,
+ /* movedToSecure= */ null, movedToSystem,
+ R.array.restore_blocked_secure_settings, dynamicBlockList,
preservedSecureSettings);
break;
case KEY_GLOBAL :
- restoreSettings(
- data,
- Settings.Global.CONTENT_URI,
- null,
- movedToSecure,
- R.array.restore_blocked_global_settings,
- dynamicBlockList,
- preservedGlobalSettings);
+ restoreSettings(data, Settings.Global.CONTENT_URI, /* movedToGlobal= */ null,
+ movedToSecure, movedToSystem, R.array.restore_blocked_global_settings,
+ dynamicBlockList, preservedGlobalSettings);
break;
case KEY_WIFI_SUPPLICANT :
@@ -395,6 +389,9 @@
break;
case KEY_SIM_SPECIFIC_SETTINGS:
+ // Intentional fall through so that sim-specific backups from Android 12 will
+ // also be restored on newer Android versions.
+ case KEY_SIM_SPECIFIC_SETTINGS_2:
byte[] restoredSimSpecificSettings = new byte[size];
data.readEntityData(restoredSimSpecificSettings, 0, size);
restoreSimSpecificSettings(restoredSimSpecificSettings);
@@ -435,10 +432,9 @@
if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
if (version <= FULL_BACKUP_VERSION) {
// Generate the moved-to-global lookup table
- HashSet<String> movedToGlobal = new HashSet<String>();
- Settings.System.getMovedToGlobalSettings(movedToGlobal);
- Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
+ Set<String> movedToGlobal = getMovedToGlobalSettings();
Set<String> movedToSecure = getMovedToSecureSettings();
+ Set<String> movedToSystem = getMovedToSystemSettings();
// system settings data first
int nBytes = in.readInt();
@@ -446,22 +442,19 @@
byte[] buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
- movedToSecure, R.array.restore_blocked_system_settings,
- Collections.emptySet(), Collections.emptySet());
+ movedToSecure, /* movedToSystem= */ null,
+ R.array.restore_blocked_system_settings, Collections.emptySet(),
+ Collections.emptySet());
// secure settings
nBytes = in.readInt();
if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
- restoreSettings(
- buffer,
- nBytes,
- Settings.Secure.CONTENT_URI,
- movedToGlobal,
- null,
- R.array.restore_blocked_secure_settings,
- Collections.emptySet(), Collections.emptySet());
+ restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal,
+ /* movedToSecure= */ null, movedToSystem,
+ R.array.restore_blocked_secure_settings, Collections.emptySet(),
+ Collections.emptySet());
// Global only if sufficiently new
if (version >= FULL_BACKUP_ADDED_GLOBAL) {
@@ -469,10 +462,10 @@
if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of global settings data");
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
- movedToGlobal.clear(); // no redirection; this *is* the global namespace
- restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal,
- movedToSecure, R.array.restore_blocked_global_settings,
- Collections.emptySet(), Collections.emptySet());
+ restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI,
+ /* movedToGlobal= */ null, movedToSecure, movedToSystem,
+ R.array.restore_blocked_global_settings, Collections.emptySet(),
+ Collections.emptySet());
}
// locale
@@ -543,6 +536,13 @@
}
}
+ private Set<String> getMovedToGlobalSettings() {
+ HashSet<String> movedToGlobalSettings = new HashSet<String>();
+ Settings.System.getMovedToGlobalSettings(movedToGlobalSettings);
+ Settings.Secure.getMovedToGlobalSettings(movedToGlobalSettings);
+ return movedToGlobalSettings;
+ }
+
private Set<String> getMovedToSecureSettings() {
Set<String> movedToSecureSettings = new HashSet<>();
Settings.Global.getMovedToSecureSettings(movedToSecureSettings);
@@ -550,6 +550,13 @@
return movedToSecureSettings;
}
+ private Set<String> getMovedToSystemSettings() {
+ Set<String> movedToSystemSettings = new HashSet<>();
+ Settings.Global.getMovedToSystemSettings(movedToSystemSettings);
+ Settings.Secure.getMovedToSystemSettings(movedToSystemSettings);
+ return movedToSystemSettings;
+ }
+
private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
long[] stateChecksums = new long[STATE_SIZE];
@@ -710,8 +717,9 @@
private void restoreSettings(
BackupDataInput data,
Uri contentUri,
- HashSet<String> movedToGlobal,
+ Set<String> movedToGlobal,
Set<String> movedToSecure,
+ Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
Set<String> settingsToPreserve) {
@@ -728,6 +736,7 @@
contentUri,
movedToGlobal,
movedToSecure,
+ movedToSystem,
blockedSettingsArrayId,
dynamicBlockList,
settingsToPreserve);
@@ -737,8 +746,9 @@
byte[] settings,
int bytes,
Uri contentUri,
- HashSet<String> movedToGlobal,
+ Set<String> movedToGlobal,
Set<String> movedToSecure,
+ Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
Set<String> settingsToPreserve) {
@@ -749,6 +759,7 @@
contentUri,
movedToGlobal,
movedToSecure,
+ movedToSystem,
blockedSettingsArrayId,
dynamicBlockList,
settingsToPreserve);
@@ -760,8 +771,9 @@
int pos,
int bytes,
Uri contentUri,
- HashSet<String> movedToGlobal,
+ Set<String> movedToGlobal,
Set<String> movedToSecure,
+ Set<String> movedToSystem,
int blockedSettingsArrayId,
Set<String> dynamicBlockList,
Set<String> settingsToPreserve) {
@@ -842,6 +854,8 @@
destination = Settings.Global.CONTENT_URI;
} else if (movedToSecure != null && movedToSecure.contains(key)) {
destination = Settings.Secure.CONTENT_URI;
+ } else if (movedToSystem != null && movedToSystem.contains(key)) {
+ destination = Settings.System.CONTENT_URI;
} else {
destination = contentUri;
}
@@ -1192,6 +1206,7 @@
Settings.Secure.CONTENT_URI,
null,
null,
+ null,
blockedSettingsArrayId,
dynamicBlocklist,
preservedSettings);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 5d75d4f..a67b565 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1620,7 +1620,11 @@
p.end(token);
// Please insert new settings using the same order as in GlobalSettingsProto.
+ // The rest of the settings were moved to Settings.Secure or Settings.System, and are thus
+ // excluded here since they're deprecated from Settings.Global.
+
// Settings.Global.INSTALL_NON_MARKET_APPS intentionally excluded since it's deprecated.
+ // Settings.Global.APPLY_RAMPING_RINGER intentionally excluded since it's deprecated.
}
private static void dumpProtoConfigSettingsLocked(
@@ -2953,6 +2957,10 @@
Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+ dumpSetting(s, p,
+ Settings.System.APPLY_RAMPING_RINGER,
+ SystemSettingsProto.APPLY_RAMPING_RINGER);
+
// Please insert new settings using the same order as in SecureSettingsProto.
// The rest of the settings were moved to Settings.Secure, and are thus excluded here since
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ce7517f..11e4916 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -289,6 +289,12 @@
Settings.Global.getMovedToSecureSettings(sGlobalMovedToSecureSettings);
}
+ // Per all users global settings that moved to the per user system settings.
+ static final Set<String> sGlobalMovedToSystemSettings = new ArraySet<>();
+ static {
+ Settings.Global.getMovedToSystemSettings(sGlobalMovedToSystemSettings);
+ }
+
// Per user secure settings that are cloned for the managed profiles of the user.
private static final Set<String> sSecureCloneToManagedSettings = new ArraySet<>();
static {
@@ -2604,6 +2610,10 @@
if (sGlobalMovedToSecureSettings.contains(name)) {
table = TABLE_SECURE;
}
+
+ if (sGlobalMovedToSystemSettings.contains(name)) {
+ table = TABLE_SYSTEM;
+ }
}
return table;
@@ -3594,7 +3604,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 206;
+ private static final int SETTINGS_VERSION = 207;
private final int mUserId;
@@ -4611,18 +4621,7 @@
if (currentVersion == 174) {
// Version 174: Set the default value for Global Settings: APPLY_RAMPING_RINGER
-
- final SettingsState globalSettings = getGlobalSettingsLocked();
-
- Setting currentRampingRingerSetting = globalSettings.getSettingLocked(
- Settings.Global.APPLY_RAMPING_RINGER);
- if (currentRampingRingerSetting.isNull()) {
- globalSettings.insertSettingOverrideableByRestoreLocked(
- Settings.Global.APPLY_RAMPING_RINGER,
- getContext().getResources().getBoolean(
- R.bool.def_apply_ramping_ringer) ? "1" : "0", null,
- true, SettingsState.SYSTEM_PACKAGE_NAME);
- }
+ // Removed. Moved APPLY_RAMPING_RINGER to System Settings, set in version 206.
currentVersion = 175;
}
@@ -5435,6 +5434,34 @@
currentVersion = 206;
}
+ if (currentVersion == 206) {
+ // Version 206: APPLY_RAMPING_RINGER moved to System settings. Use the old value
+ // for the newly inserted system setting and keep it to be restored to other
+ // users. Set default value if global value is not set.
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ Setting globalValue = getGlobalSettingsLocked()
+ .getSettingLocked(Global.APPLY_RAMPING_RINGER);
+ Setting currentValue = systemSettings
+ .getSettingLocked(Settings.System.APPLY_RAMPING_RINGER);
+ if (currentValue.isNull()) {
+ if (!globalValue.isNull()) {
+ // Recover settings from Global.
+ systemSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.System.APPLY_RAMPING_RINGER, globalValue.getValue(),
+ globalValue.getTag(), globalValue.isDefaultFromSystem(),
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ } else {
+ // Set default value.
+ systemSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.System.APPLY_RAMPING_RINGER,
+ getContext().getResources().getBoolean(
+ R.bool.def_apply_ramping_ringer) ? "1" : "0",
+ null /* tag */, true /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+ currentVersion = 207;
+ }
// vXXX: Add new settings above this point.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4aee164..aa6661b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -345,7 +345,6 @@
}
// The settings provider must hold its lock when calling here.
- @GuardedBy("mLock")
public Setting getSettingLocked(String name) {
if (TextUtils.isEmpty(name)) {
return mNullSetting;
@@ -385,7 +384,6 @@
}
// The settings provider must hold its lock when calling here.
- @GuardedBy("mLock")
public boolean insertSettingOverrideableByRestoreLocked(String name, String value, String tag,
boolean makeDefault, String packageName) {
return insertSettingLocked(name, value, tag, makeDefault, false, packageName,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index f5334fb..433aac7 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -205,8 +205,8 @@
mAgentUnderTest.mSettingsHelper = settingsHelper;
byte[] backupData = generateBackupData(TEST_VALUES);
- mAgentUnderTest.restoreSettings(backupData, /* pos */ 0, backupData.length, TEST_URI, new HashSet<>(),
- Collections.emptySet(), /* blockedSettingsArrayId */ 0, Collections.emptySet(),
+ mAgentUnderTest.restoreSettings(backupData, /* pos */ 0, backupData.length, TEST_URI,
+ null, null, null, /* blockedSettingsArrayId */ 0, Collections.emptySet(),
new HashSet<>(Collections.singletonList(SettingsBackupAgent.getQualifiedKeyForSetting(PRESERVED_TEST_SETTING, TEST_URI))));
assertTrue(settingsHelper.mWrittenValues.containsKey(OVERRIDDEN_TEST_SETTING));
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index abd010d..2b311ee 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -90,7 +90,6 @@
<uses-permission android:name="android.permission.RESTART_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND" />
- <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<uses-permission android:name="android.permission.HIDE_OVERLAY_WINDOWS" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
@@ -205,6 +204,7 @@
<uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+ <uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" />
<uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" />
<uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" />
<uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
@@ -297,9 +297,13 @@
<!-- Permission needed to wipe the device for Test Harness Mode -->
<uses-permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE" />
- <!-- Permissions required to test CompanionDeviceManager teses in CTS -->
- <uses-permission android:name="android.permission.MANAGE_COMPANION_DEVICES" />
+ <!-- Permission needed for CTS test - CompanionDeviceManagerTest -->
<uses-permission android:name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS" />
+ <uses-permission android:name="android.permission.MANAGE_COMPANION_DEVICES" />
+ <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING" />
+ <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION" />
+ <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
+ <uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
<uses-permission android:name="android.permission.MANAGE_APPOPS" />
<uses-permission android:name="android.permission.WATCH_APPOPS" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 8c70112..ee9d430 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -150,6 +150,7 @@
// Internal intents used on notification actions.
static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
static final String INTENT_BUGREPORT_SHARE = "android.intent.action.BUGREPORT_SHARE";
+ static final String INTENT_BUGREPORT_DONE = "android.intent.action.BUGREPORT_DONE";
static final String INTENT_BUGREPORT_INFO_LAUNCH =
"android.intent.action.BUGREPORT_INFO_LAUNCH";
static final String INTENT_BUGREPORT_SCREENSHOT =
@@ -555,6 +556,8 @@
case INTENT_BUGREPORT_SHARE:
shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
break;
+ case INTENT_BUGREPORT_DONE:
+ maybeShowWarningMessageAndCloseNotification(id);
case INTENT_BUGREPORT_CANCEL:
cancel(id);
break;
@@ -809,10 +812,30 @@
}
/**
+ * Creates a {@link PendingIntent} for a notification action used to show warning about the
+ * sensitivity of bugreport data and then close bugreport notification.
+ *
+ * Note that, the warning message may not be shown if the user has chosen not to see the
+ * message anymore.
+ */
+ private static PendingIntent newBugreportDoneIntent(Context context, BugreportInfo info) {
+ final Intent intent = new Intent(INTENT_BUGREPORT_DONE);
+ intent.setClass(context, BugreportProgressService.class);
+ intent.putExtra(EXTRA_ID, info.id);
+ return PendingIntent.getService(context, info.id, intent,
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ @GuardedBy("mLock")
+ private void stopProgressLocked(int id) {
+ stopProgressLocked(id, /* cancelNotification */ true);
+ }
+
+ /**
* Finalizes the progress on a given bugreport and cancel its notification.
*/
@GuardedBy("mLock")
- private void stopProgressLocked(int id) {
+ private void stopProgressLocked(int id, boolean cancelNotification) {
if (mBugreportInfos.indexOfKey(id) < 0) {
Log.w(TAG, "ID not watched: " + id);
} else {
@@ -821,8 +844,13 @@
}
// Must stop foreground service first, otherwise notif.cancel() will fail below.
stopForegroundWhenDoneLocked(id);
- Log.d(TAG, "stopProgress(" + id + "): cancel notification");
- NotificationManager.from(mContext).cancel(id);
+
+ if (cancelNotification) {
+ Log.d(TAG, "stopProgress(" + id + "): cancel notification");
+ NotificationManager.from(mContext).cancel(id);
+ } else {
+ Log.d(TAG, "stopProgress(" + id + ")");
+ }
stopSelfWhenDoneLocked();
}
@@ -1039,7 +1067,8 @@
}
/**
- * Wraps up bugreport generation and triggers a notification to share the bugreport.
+ * Wraps up bugreport generation and triggers a notification to either share the bugreport or
+ * just notify the ending of the bugreport generation, according to the device type.
*/
private void onBugreportFinished(BugreportInfo info) {
if (!TextUtils.isEmpty(info.shareTitle)) {
@@ -1054,25 +1083,46 @@
stopForegroundWhenDoneLocked(info.id);
}
- triggerLocalNotification(mContext, info);
- }
-
- /**
- * Responsible for triggering a notification that allows the user to start a "share" intent with
- * the bugreport. On watches we have other methods to allow the user to start this intent
- * (usually by triggering it on another connected device); we don't need to display the
- * notification in this case.
- */
- private void triggerLocalNotification(final Context context, final BugreportInfo info) {
if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
- Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
+ Toast.makeText(mContext, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
synchronized (mLock) {
stopProgressLocked(info.id);
}
return;
}
+ if (mIsWatch) {
+ // Wear wants to send the notification directly and not wait for the user to tap on the
+ // notification.
+ triggerShareBugreportAndLocalNotification(info);
+ } else {
+ triggerLocalNotification(info);
+ }
+ }
+
+ /**
+ * Responsible for starting the bugerport sharing process and posting a notification which
+ * shows that the bugreport has been taken and that the sharing process has kicked-off.
+ */
+ private void triggerShareBugreportAndLocalNotification(final BugreportInfo info) {
+ boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt");
+ if (!isPlainText) {
+ // Already zipped, share it right away.
+ shareBugreport(info.id, info, /* showWarning */ false,
+ /* cancelNotificationWhenStoppingProgress */ false);
+ sendBugreportNotification(info, mTakingScreenshot);
+ } else {
+ // Asynchronously zip the file first, then share it.
+ shareAndPostNotificationForZippedBugreport(info, mTakingScreenshot);
+ }
+ }
+
+ /**
+ * Responsible for triggering a notification that allows the user to start a "share" intent with
+ * the bugreport.
+ */
+ private void triggerLocalNotification(final BugreportInfo info) {
boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt");
if (!isPlainText) {
// Already zipped, send it right away.
@@ -1083,9 +1133,11 @@
}
}
- private static Intent buildWarningIntent(Context context, Intent sendIntent) {
+ private static Intent buildWarningIntent(Context context, @Nullable Intent sendIntent) {
final Intent intent = new Intent(context, BugreportWarningActivity.class);
- intent.putExtra(Intent.EXTRA_INTENT, sendIntent);
+ if (sendIntent != null) {
+ intent.putExtra(Intent.EXTRA_INTENT, sendIntent);
+ }
return intent;
}
@@ -1163,11 +1215,30 @@
return intent;
}
+ private boolean hasUserDecidedNotToGetWarningMessage() {
+ return getWarningState(mContext, STATE_UNKNOWN) == STATE_HIDE;
+ }
+
+ private void maybeShowWarningMessageAndCloseNotification(int id) {
+ if (!hasUserDecidedNotToGetWarningMessage()) {
+ Intent warningIntent = buildWarningIntent(mContext, /* sendIntent */ null);
+ warningIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(warningIntent);
+ }
+ NotificationManager.from(mContext).cancel(id);
+ }
+
+ private void shareBugreport(int id, BugreportInfo sharedInfo) {
+ shareBugreport(id, sharedInfo, !hasUserDecidedNotToGetWarningMessage(),
+ /* cancelNotificationWhenStoppingProgress */ true);
+ }
+
/**
* Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
* intent, but issuing a warning dialog the first time.
*/
- private void shareBugreport(int id, BugreportInfo sharedInfo) {
+ private void shareBugreport(int id, BugreportInfo sharedInfo, boolean showWarning,
+ boolean cancelNotificationWhenStoppingProgress) {
MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SHARE);
BugreportInfo info;
synchronized (mLock) {
@@ -1199,7 +1270,7 @@
boolean useChooser = true;
// Send through warning dialog by default
- if (getWarningState(mContext, STATE_UNKNOWN) != STATE_HIDE) {
+ if (showWarning) {
notifIntent = buildWarningIntent(mContext, sendIntent);
// No need to show a chooser in this case.
useChooser = false;
@@ -1216,7 +1287,7 @@
}
synchronized (mLock) {
// ... and stop watching this process.
- stopProgressLocked(id);
+ stopProgressLocked(id, cancelNotificationWhenStoppingProgress);
}
}
@@ -1240,12 +1311,6 @@
// Since adding the details can take a while, do it before notifying user.
addDetailsToZipFile(info);
- final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE);
- shareIntent.setClass(mContext, BugreportProgressService.class);
- shareIntent.setAction(INTENT_BUGREPORT_SHARE);
- shareIntent.putExtra(EXTRA_ID, info.id);
- shareIntent.putExtra(EXTRA_INFO, info);
-
String content;
content = takingScreenshot ?
mContext.getString(R.string.bugreport_finished_pending_screenshot_text)
@@ -1263,11 +1328,32 @@
final Notification.Builder builder = newBaseNotification(mContext)
.setContentTitle(title)
.setTicker(title)
- .setContentText(content)
- .setContentIntent(PendingIntent.getService(mContext, info.id, shareIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.setOnlyAlertOnce(false)
- .setDeleteIntent(newCancelIntent(mContext, info));
+ .setContentText(content);
+
+ if (!mIsWatch) {
+ final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE);
+ shareIntent.setClass(mContext, BugreportProgressService.class);
+ shareIntent.setAction(INTENT_BUGREPORT_SHARE);
+ shareIntent.putExtra(EXTRA_ID, info.id);
+ shareIntent.putExtra(EXTRA_INFO, info);
+
+ builder.setContentIntent(PendingIntent.getService(mContext, info.id, shareIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
+ .setDeleteIntent(newCancelIntent(mContext, info));
+ } else {
+ // Device is a watch.
+ if (hasUserDecidedNotToGetWarningMessage()) {
+ // No action button needed for the notification. User can swipe to dimiss.
+ builder.setActions(new Action[0]);
+ } else {
+ // Add action button to lead user to the warning screen.
+ builder.setActions(
+ new Action.Builder(
+ null, mContext.getString(R.string.bugreport_info_action),
+ newBugreportDoneIntent(mContext, info)).build());
+ }
+ }
if (!TextUtils.isEmpty(info.getName())) {
builder.setSubText(info.getName());
@@ -1327,6 +1413,24 @@
}
/**
+ * Zips a bugreport, shares it, and sends for it a bugreport notification.
+ */
+ private void shareAndPostNotificationForZippedBugreport(final BugreportInfo info,
+ final boolean takingScreenshot) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ Looper.prepare();
+ zipBugreport(info);
+ shareBugreport(info.id, info, /* showWarning */ false,
+ /* cancelNotificationWhenStoppingProgress */ false);
+ sendBugreportNotification(info, mTakingScreenshot);
+ return null;
+ }
+ }.execute();
+ }
+
+ /**
* Zips a bugreport file, returning the path to the new file (or to the
* original in case of failure).
*/
diff --git a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
index ecd1369..a44e236 100644
--- a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
+++ b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
@@ -54,9 +54,11 @@
mSendIntent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
- // We need to touch the extras to unpack them so they get migrated to
- // ClipData correctly.
- mSendIntent.hasExtra(Intent.EXTRA_STREAM);
+ if (mSendIntent != null) {
+ // We need to touch the extras to unpack them so they get migrated to
+ // ClipData correctly.
+ mSendIntent.hasExtra(Intent.EXTRA_STREAM);
+ }
final AlertController.AlertParams ap = mAlertParams;
ap.mView = LayoutInflater.from(this).inflate(R.layout.confirm_repeat, null);
@@ -84,7 +86,9 @@
if (which == AlertDialog.BUTTON_POSITIVE) {
// Remember confirm state, and launch target
setWarningState(this, mConfirmRepeat.isChecked() ? STATE_HIDE : STATE_SHOW);
- sendShareIntent(this, mSendIntent);
+ if (mSendIntent != null) {
+ sendShareIntent(this, mSendIntent);
+ }
}
finish();
diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml b/packages/SystemUI/animation/res/anim/launch_dialog_enter.xml
similarity index 100%
rename from packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml
rename to packages/SystemUI/animation/res/anim/launch_dialog_enter.xml
diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml b/packages/SystemUI/animation/res/anim/launch_dialog_exit.xml
similarity index 100%
rename from packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml
rename to packages/SystemUI/animation/res/anim/launch_dialog_exit.xml
diff --git a/packages/SystemUI/animation/res/values/ids.xml b/packages/SystemUI/animation/res/values/ids.xml
index c4cb89f..ef60a24 100644
--- a/packages/SystemUI/animation/res/values/ids.xml
+++ b/packages/SystemUI/animation/res/values/ids.xml
@@ -16,5 +16,4 @@
-->
<resources>
<item type="id" name="launch_animation_running"/>
- <item type="id" name="dialog_content_parent" />
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/values/styles.xml b/packages/SystemUI/animation/res/values/styles.xml
index ad06c91..3b3f7f6 100644
--- a/packages/SystemUI/animation/res/values/styles.xml
+++ b/packages/SystemUI/animation/res/values/styles.xml
@@ -15,15 +15,10 @@
limitations under the License.
-->
<resources>
- <style name="HostDialogTheme">
- <item name="android:windowAnimationStyle">@style/Animation.HostDialog</item>
- <item name="android:windowIsFloating">false</item>
- <item name="android:backgroundDimEnabled">true</item>
- <item name="android:navigationBarColor">@android:color/transparent</item>
- </style>
-
- <style name="Animation.HostDialog" parent="@android:style/Animation">
- <item name="android:windowEnterAnimation">@anim/launch_host_dialog_enter</item>
- <item name="android:windowExitAnimation">@anim/launch_host_dialog_exit</item>
+ <!-- An animation used by DialogLaunchAnimator to make a dialog appear instantly (to animate -->
+ <!-- in-window) and disappear by fading out (when the exit into view is disabled). -->
+ <style name="Animation.LaunchAnimation" parent="@android:style/Animation">
+ <item name="android:windowEnterAnimation">@anim/launch_dialog_enter</item>
+ <item name="android:windowExitAnimation">@anim/launch_dialog_exit</item>
</style>
</resources>
\ No newline at end of file
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 9aad278..de82ebd 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -24,23 +24,19 @@
import android.graphics.Color
import android.graphics.Rect
import android.os.Looper
+import android.service.dreams.IDreamManager
import android.util.Log
import android.util.MathUtils
import android.view.GhostView
-import android.view.Gravity
import android.view.View
import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewTreeObserver.OnPreDrawListener
-import android.view.WindowInsets
import android.view.WindowManager
-import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-import android.view.WindowManagerPolicyConstants
import android.widget.FrameLayout
import kotlin.math.roundToInt
private const val TAG = "DialogLaunchAnimator"
-private val DIALOG_CONTENT_PARENT_ID = R.id.dialog_content_parent
/**
* A class that allows dialogs to be started in a seamless way from a view that is transforming
@@ -49,7 +45,7 @@
class DialogLaunchAnimator(
private val context: Context,
private val launchAnimator: LaunchAnimator,
- private val hostDialogProvider: HostDialogProvider
+ private val dreamManager: IDreamManager
) {
private companion object {
private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.launch_animation_running
@@ -63,40 +59,38 @@
private val openedDialogs = hashSetOf<AnimatedDialog>()
/**
- * Show [dialog] by expanding it from [view]. If [animateBackgroundBoundsChange] is true, then
- * the background of the dialog will be animated when the dialog bounds change.
+ * Show [dialog] by expanding it from [view]. If [view] is a view inside another dialog that was
+ * shown using this method, then we will animate from that dialog instead.
*
- * Caveats: When calling this function, the dialog content view will actually be stolen and
- * attached to a different dialog (and thus a different window) which means that the actual
- * dialog window will never be drawn. Moreover, unless [dialog] is a [ListenableDialog], you
- * must call dismiss(), hide() and show() on the [Dialog] returned by this function to actually
- * dismiss, hide or show the dialog.
+ * If [animateBackgroundBoundsChange] is true, then the background of the dialog will be
+ * animated when the dialog bounds change.
+ *
+ * Caveats: When calling this function and [dialog] is not a fullscreen dialog, then it will be
+ * made fullscreen and 2 views will be inserted between the dialog DecorView and its children.
*/
@JvmOverloads
fun showFromView(
dialog: Dialog,
view: View,
animateBackgroundBoundsChange: Boolean = false
- ): Dialog {
+ ) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw IllegalStateException(
"showFromView must be called from the main thread and dialog must be created in " +
"the main thread")
}
- // If the parent of the view we are launching from is the background of some other animated
- // dialog, then this means the caller intent is to launch a dialog from another dialog. In
- // this case, we also animate the parent (which is the dialog background).
+ // If the view we are launching from belongs to another dialog, then this means the caller
+ // intent is to launch a dialog from another dialog.
val animatedParent = openedDialogs
- .firstOrNull { it.dialogContentParent == view.parent }
- val parentHostDialog = animatedParent?.hostDialog
- val animateFrom = animatedParent?.dialogContentParent ?: view
+ .firstOrNull { it.dialog.window.decorView.viewRootImpl == view.viewRootImpl }
+ val animateFrom = animatedParent?.dialogContentWithBackground ?: view
// Make sure we don't run the launch animation from the same view twice at the same time.
if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
Log.e(TAG, "Not running dialog launch animation as there is already one running")
dialog.show()
- return dialog
+ return
}
animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
@@ -104,82 +98,36 @@
val animatedDialog = AnimatedDialog(
context,
launchAnimator,
- hostDialogProvider,
+ dreamManager,
animateFrom,
onDialogDismissed = { openedDialogs.remove(it) },
- originalDialog = dialog,
+ dialog = dialog,
animateBackgroundBoundsChange,
- openedDialogs.firstOrNull { it.hostDialog == parentHostDialog }
+ animatedParent
)
- val hostDialog = animatedDialog.hostDialog
+
openedDialogs.add(animatedDialog)
-
- // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the
- // host dialog.
- if (dialog is ListenableDialog) {
- dialog.addListener(object : DialogListener {
- override fun onDismiss(reason: DialogListener.DismissReason) {
- dialog.removeListener(this)
-
- // We disable the exit animation if we are dismissing the dialog because the
- // device is being locked, otherwise the animation looks bad if AOD is enabled.
- // If AOD is disabled the screen will directly becomes black and we won't see
- // the animation anyways.
- if (reason == DialogListener.DismissReason.DEVICE_LOCKED) {
- animatedDialog.exitAnimationDisabled = true
- }
-
- hostDialog.dismiss()
- }
-
- override fun onHide() {
- if (animatedDialog.ignoreNextCallToHide) {
- animatedDialog.ignoreNextCallToHide = false
- return
- }
-
- hostDialog.hide()
- }
-
- override fun onShow() {
- hostDialog.show()
-
- // We don't actually want to show the original dialog, so hide it.
- animatedDialog.ignoreNextCallToHide = true
- dialog.hide()
- }
-
- override fun onSizeChanged() {
- animatedDialog.onOriginalDialogSizeChanged()
- }
-
- override fun prepareForStackDismiss() {
- animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss()
- }
- })
- }
-
animatedDialog.start()
- return hostDialog
}
/**
- * Launch [dialog] from a [parentHostDialog] as returned by [showFromView]. This will allow
- * for dismissing the whole stack.
+ * Launch [dialog] from [another dialog][animateFrom] that was shown using [showFromView]. This
+ * will allow for dismissing the whole stack.
*
- * This will return a new host dialog, with the same caveat as [showFromView].
- *
- * @see DialogListener.prepareForStackDismiss
+ * @see dismissStack
*/
fun showFromDialog(
dialog: Dialog,
- parentHostDialog: Dialog,
+ animateFrom: Dialog,
animateBackgroundBoundsChange: Boolean = false
- ): Dialog {
- val view = parentHostDialog.findViewById<ViewGroup>(DIALOG_CONTENT_PARENT_ID)
- ?.getChildAt(0)
- ?: throw IllegalStateException("No dialog content parent found in host dialog")
- return showFromView(dialog, view, animateBackgroundBoundsChange)
+ ) {
+ val view = openedDialogs
+ .firstOrNull { it.dialog == animateFrom }
+ ?.dialogContentWithBackground
+ ?: throw IllegalStateException(
+ "The animateFrom dialog was not animated using " +
+ "DialogLaunchAnimator.showFrom(View|Dialog)")
+ showFromView(dialog, view, animateBackgroundBoundsChange)
}
/**
@@ -195,69 +143,23 @@
fun disableAllCurrentDialogsExitAnimations() {
openedDialogs.forEach { it.exitAnimationDisabled = true }
}
-}
-interface HostDialogProvider {
/**
- * Create a host dialog that will be used to host a launch animation. This host dialog must:
- * 1. call [onCreateCallback] in its onCreate() method, e.g. right after calling
- * super.onCreate().
- * 2. call [dismissOverride] instead of doing any dismissing logic. The actual dismissing
- * logic should instead be done inside the lambda passed to [dismissOverride], which will
- * be called after the exit animation.
- * 3. Be full screen, i.e. have a window matching its parent size.
- *
- * See SystemUIHostDialogProvider for an example of implementation.
+ * Dismiss [dialog]. If it was launched from another dialog using [showFromView], also dismiss
+ * the stack of dialogs, animating back to the original touchSurface.
*/
- fun createHostDialog(
- context: Context,
- theme: Int,
- onCreateCallback: () -> Unit,
- dismissOverride: (() -> Unit) -> Unit
- ): Dialog
-}
-
-/** A dialog to/from which we can add/remove listeners. */
-interface ListenableDialog {
- /** Add [listener] to the listeners. */
- fun addListener(listener: DialogListener)
-
- /** Remove [listener] from the listeners. */
- fun removeListener(listener: DialogListener)
-}
-
-interface DialogListener {
- /** The reason why a dialog was dismissed. */
- enum class DismissReason {
- UNKNOWN,
-
- /** The device was locked, which dismissed this dialog. */
- DEVICE_LOCKED,
+ fun dismissStack(dialog: Dialog) {
+ openedDialogs
+ .firstOrNull { it.dialog == dialog }
+ ?.let { it.touchSurface = it.prepareForStackDismiss() }
+ dialog.dismiss()
}
-
- /** Called when this dialog dismiss() is called. */
- fun onDismiss(reason: DismissReason)
-
- /** Called when this dialog hide() is called. */
- fun onHide()
-
- /** Called when this dialog show() is called. */
- fun onShow()
-
- /**
- * Call before dismissing a stack of dialogs (dialogs launched from dialogs), so the topmost
- * can animate directly into the original `touchSurface`.
- */
- fun prepareForStackDismiss()
-
- /** Called when this dialog size might have changed, e.g. because of configuration changes. */
- fun onSizeChanged()
}
private class AnimatedDialog(
private val context: Context,
private val launchAnimator: LaunchAnimator,
- hostDialogProvider: HostDialogProvider,
+ private val dreamManager: IDreamManager,
/** The view that triggered the dialog after being tapped. */
var touchSurface: View,
@@ -268,37 +170,33 @@
*/
private val onDialogDismissed: (AnimatedDialog) -> Unit,
- /** The original dialog whose content will be shown and animate in/out in [hostDialog]. */
- private val originalDialog: Dialog,
+ /** The dialog to show and animate. */
+ val dialog: Dialog,
/** Whether we should animate the dialog background when its bounds change. */
private val animateBackgroundBoundsChange: Boolean,
- /** Launch animation corresponding to the parent [hostDialog]. */
+ /** Launch animation corresponding to the parent [AnimatedDialog]. */
private val parentAnimatedDialog: AnimatedDialog? = null
) {
/**
- * The fullscreen dialog to which we will add the content view [originalDialogView] of
- * [originalDialog].
- */
- val hostDialog = hostDialogProvider.createHostDialog(
- context, R.style.HostDialogTheme, this::onHostDialogCreated, this::onHostDialogDismissed)
-
- /** The root content view of [hostDialog]. */
- private val hostDialogRoot = FrameLayout(context)
+ * The DecorView of this dialog window.
+ *
+ * Note that we access this DecorView lazily to avoid accessing it before the dialog is created,
+ * which can sometimes cause crashes (e.g. with the Cast dialog).
+ */
+ private val decorView by lazy { dialog.window!!.decorView as ViewGroup }
/**
- * The parent of the original dialog content view, that serves as a fake window that will have
- * the same size as the original dialog window and to which we will set the original dialog
- * window background.
+ * The dialog content with its background. When animating a fullscreen dialog, this is just the
+ * first ViewGroup of the dialog that has a background. When animating a normal (not fullscreen)
+ * dialog, this is an additional view that serves as a fake window that will have the same size
+ * as the dialog window initially had and to which we will set the dialog window background.
*/
- val dialogContentParent = FrameLayout(context).apply {
- id = DIALOG_CONTENT_PARENT_ID
- }
+ var dialogContentWithBackground: ViewGroup? = null
/**
- * The background color of [originalDialogView], taking into consideration the [originalDialog]
- * window background color.
+ * The background color of [dialog], taking into consideration its window background color.
*/
private var originalDialogBackgroundColor = Color.BLACK
@@ -311,75 +209,182 @@
private var isDismissing = false
private var dismissRequested = false
- var ignoreNextCallToHide = false
var exitAnimationDisabled = false
private var isTouchSurfaceGhostDrawn = false
private var isOriginalDialogViewLaidOut = false
- private var backgroundLayoutListener = if (animateBackgroundBoundsChange) {
+
+ /** A layout listener to animate the dialog height change. */
+ private val backgroundLayoutListener = if (animateBackgroundBoundsChange) {
AnimatedBoundsLayoutListener()
} else {
null
}
+ /*
+ * A layout listener in case the dialog (window) size changes (for instance because of a
+ * configuration change) to ensure that the dialog stays full width.
+ */
+ private var decorViewLayoutListener: View.OnLayoutChangeListener? = null
+
fun start() {
- // Show the host (fullscreen) dialog, to which we will add the stolen dialog view.
- hostDialog.show()
+ // Create the dialog so that its onCreate() method is called, which usually sets the dialog
+ // content.
+ dialog.create()
- // Steal the dialog view. We do that by showing it but preventing it from drawing, then
- // hiding it as soon as its content is available.
- stealOriginalDialogContentView(then = this::showDialogFromView)
- }
+ val window = dialog.window!!
+ val isWindowFullScreen =
+ window.attributes.width == MATCH_PARENT && window.attributes.height == MATCH_PARENT
+ val dialogContentWithBackground = if (isWindowFullScreen) {
+ // If the dialog window is already fullscreen, then we look for the first ViewGroup that
+ // has a background (and is not the DecorView, which always has a background) and
+ // animate towards that ViewGroup given that this is probably what represents the actual
+ // dialog view.
+ var viewGroupWithBackground: ViewGroup? = null
+ for (i in 0 until decorView.childCount) {
+ viewGroupWithBackground = findFirstViewGroupWithBackground(decorView.getChildAt(i))
+ if (viewGroupWithBackground != null) {
+ break
+ }
+ }
- private fun onHostDialogCreated() {
- // Make the dialog fullscreen with a transparent background.
- hostDialog.setContentView(
- hostDialogRoot,
- ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
+ // Animate that view with the background. Throw if we didn't find one, because otherwise
+ // it's not clear what we should animate.
+ viewGroupWithBackground
+ ?: throw IllegalStateException("Unable to find ViewGroup with background")
+ } else {
+ // We will make the dialog window (and therefore its DecorView) fullscreen to make it
+ // possible to animate outside its bounds.
+ //
+ // Before that, we add a new View as a child of the DecorView with the same size and
+ // gravity as that DecorView, then we add all original children of the DecorView to that
+ // new View. Finally we remove the background of the DecorView and add it to the new
+ // View, then we make the DecorView fullscreen. This new View now acts as a fake (non
+ // fullscreen) window.
+ //
+ // On top of that, we also add a fullscreen transparent background between the DecorView
+ // and the view that we added so that we can dismiss the dialog when this view is
+ // clicked. This is necessary because DecorView overrides onTouchEvent and therefore we
+ // can't set the click listener directly on the (now fullscreen) DecorView.
+ val fullscreenTransparentBackground = FrameLayout(context)
+ decorView.addView(
+ fullscreenTransparentBackground,
+ 0 /* index */,
+ FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
)
- )
- val window = hostDialog.window
- ?: throw IllegalStateException("There is no window associated to the host dialog")
- window.setBackgroundDrawableResource(android.R.color.transparent)
+ val dialogContentWithBackground = FrameLayout(context)
+ dialogContentWithBackground.background = decorView.background
- // If we are using gesture navigation, then we can overlay the navigation/task bars with
- // the host dialog.
- val navigationMode = context.resources.getInteger(
- com.android.internal.R.integer.config_navBarInteractionMode)
- if (navigationMode == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL) {
- window.attributes.fitInsetsTypes = window.attributes.fitInsetsTypes and
- WindowInsets.Type.navigationBars().inv()
- window.addFlags(FLAG_LAYOUT_IN_SCREEN or FLAG_LAYOUT_INSET_DECOR)
- window.setDecorFitsSystemWindows(false)
+ // Make the window background transparent. Note that setting the window (or DecorView)
+ // background drawable to null leads to issues with background color (not being
+ // transparent) or with insets that are not refreshed. Therefore we need to set it to
+ // something not null, hence we are using android.R.color.transparent here.
+ window.setBackgroundDrawableResource(android.R.color.transparent)
+
+ // Close the dialog when clicking outside of it.
+ fullscreenTransparentBackground.setOnClickListener { dialog.dismiss() }
+ dialogContentWithBackground.isClickable = true
+
+ fullscreenTransparentBackground.addView(
+ dialogContentWithBackground,
+ FrameLayout.LayoutParams(
+ window.attributes.width,
+ window.attributes.height,
+ window.attributes.gravity
+ )
+ )
+
+ // Move all original children of the DecorView to the new View we just added.
+ for (i in 1 until decorView.childCount) {
+ val view = decorView.getChildAt(1)
+ decorView.removeViewAt(1)
+ dialogContentWithBackground.addView(view)
+ }
+
+ // Make the window fullscreen and add a layout listener to ensure it stays fullscreen.
+ window.setLayout(MATCH_PARENT, MATCH_PARENT)
+ decorViewLayoutListener = View.OnLayoutChangeListener {
+ v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
+ if (window.attributes.width != MATCH_PARENT ||
+ window.attributes.height != MATCH_PARENT) {
+ // The dialog size changed, copy its size to dialogContentWithBackground and
+ // make the dialog window full screen again.
+ val layoutParams = dialogContentWithBackground.layoutParams
+ layoutParams.width = window.attributes.width
+ layoutParams.height = window.attributes.height
+ dialogContentWithBackground.layoutParams = layoutParams
+ window.setLayout(MATCH_PARENT, MATCH_PARENT)
+ }
+ }
+ decorView.addOnLayoutChangeListener(decorViewLayoutListener)
+
+ dialogContentWithBackground
}
+ this.dialogContentWithBackground = dialogContentWithBackground
+
+ val background = dialogContentWithBackground.background
+ originalDialogBackgroundColor =
+ GhostedViewLaunchAnimatorController.findGradientDrawable(background)
+ ?.color
+ ?.defaultColor ?: Color.BLACK
+
+ // Make the background view invisible until we start the animation.
+ dialogContentWithBackground.visibility = View.INVISIBLE
+
+ // Make sure the dialog is visible instantly and does not do any window animation.
+ window.attributes.windowAnimations = R.style.Animation_LaunchAnimation
+
+ // Start the animation once the background view is properly laid out.
+ dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ dialogContentWithBackground.removeOnLayoutChangeListener(this)
+
+ isOriginalDialogViewLaidOut = true
+ maybeStartLaunchAnimation()
+ }
+ })
// Disable the dim. We will enable it once we start the animation.
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ // Override the dialog dismiss() so that we can animate the exit before actually dismissing
+ // the dialog.
+ dialog.setDismissOverride(this::onDialogDismissed)
+
+ // Show the dialog.
+ dialog.show()
+
// Add a temporary touch surface ghost as soon as the window is ready to draw. This
- // temporary ghost will be drawn together with the touch surface, but in the host dialog
+ // temporary ghost will be drawn together with the touch surface, but in the dialog
// window. Once it is drawn, we will make the touch surface invisible, and then start the
// animation. We do all this synchronization to avoid flicker that would occur if we made
// the touch surface invisible too early (before its ghost is drawn), leading to one or more
// frames with a hole instead of the touch surface (or its ghost).
- hostDialogRoot.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
+ decorView.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
override fun onPreDraw(): Boolean {
- hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this)
+ decorView.viewTreeObserver.removeOnPreDrawListener(this)
addTemporaryTouchSurfaceGhost()
return true
}
})
- hostDialogRoot.invalidate()
+ decorView.invalidate()
}
private fun addTemporaryTouchSurfaceGhost() {
// Create a ghost of the touch surface (which will make the touch surface invisible) and add
- // it to the host dialog. We will wait for this ghost to be drawn before starting the
- // animation.
- val ghost = GhostView.addGhost(touchSurface, hostDialogRoot)
+ // it to the dialog. We will wait for this ghost to be drawn before starting the animation.
+ val ghost = GhostView.addGhost(touchSurface, decorView)
// The ghost of the touch surface was just created, so the touch surface was made invisible.
// We make it visible again until the ghost is actually drawn.
@@ -415,125 +420,23 @@
touchSurface.invalidate()
}
- /** Get the content view of [originalDialog] and pass it to [then]. */
- private fun stealOriginalDialogContentView(then: (View) -> Unit) {
- // The original dialog content view will be attached to android.R.id.content when the dialog
- // is shown, so we show the dialog and add an observer to get the view but also prevents the
- // original dialog from being drawn.
- val androidContent = originalDialog.findViewById<ViewGroup>(android.R.id.content)
- ?: throw IllegalStateException("Dialog does not have any android.R.id.content view")
-
- androidContent.viewTreeObserver.addOnPreDrawListener(
- object : OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- if (androidContent.childCount == 1) {
- androidContent.viewTreeObserver.removeOnPreDrawListener(this)
-
- // Hide the animated dialog. Because of the dialog listener set up
- // earlier, this would also hide the host dialog, but in this case we
- // need to keep the host dialog visible.
- ignoreNextCallToHide = true
- originalDialog.hide()
-
- then(androidContent.getChildAt(0))
- return false
- }
-
- // Never draw the original dialog content.
- return false
- }
- })
- originalDialog.show()
- }
-
- private fun showDialogFromView(dialogView: View) {
- // Close the dialog when clicking outside of it.
- hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
- dialogView.isClickable = true
-
- // Set the background of the window dialog to the dialog itself.
- // TODO(b/193634619): Support dialog windows without background.
- // TODO(b/193634619): Support dialog whose background comes from the content view instead of
- // the window.
- val typedArray =
- originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window)
- val backgroundRes =
- typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0)
- typedArray.recycle()
- if (backgroundRes == 0) {
- throw IllegalStateException("Dialogs with no backgrounds on window are not supported")
+ private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
+ if (view !is ViewGroup) {
+ return null
}
- // Add a parent view to the original dialog view to which we will set the original dialog
- // window background. This View serves as a fake window with background, so that we are sure
- // that we don't override the dialog view paddings with the window background that usually
- // has insets.
- dialogContentParent.setBackgroundResource(backgroundRes)
- hostDialogRoot.addView(
- dialogContentParent,
+ if (view.background != null) {
+ return view
+ }
- // We give it the size of its original dialog window.
- FrameLayout.LayoutParams(
- originalDialog.window.attributes.width,
- originalDialog.window.attributes.height,
- Gravity.CENTER
- )
- )
-
- // Make the dialog view parent invisible for now, to make sure it's not drawn yet.
- dialogContentParent.visibility = View.INVISIBLE
-
- val background = dialogContentParent.background!!
- originalDialogBackgroundColor =
- GhostedViewLaunchAnimatorController.findGradientDrawable(background)
- ?.color
- ?.defaultColor ?: Color.BLACK
-
- // Add the dialog view to its parent (that has the original window background).
- (dialogView.parent as? ViewGroup)?.removeView(dialogView)
- dialogContentParent.addView(
- dialogView,
-
- // It should match its parent size, which is sized the same as the original dialog
- // window.
- FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- )
- )
-
- // Start the animation when the dialog is laid out in the center of the host dialog.
- dialogContentParent.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
- override fun onLayoutChange(
- view: View,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- dialogContentParent.removeOnLayoutChangeListener(this)
-
- isOriginalDialogViewLaidOut = true
- maybeStartLaunchAnimation()
+ for (i in 0 until view.childCount) {
+ val match = findFirstViewGroupWithBackground(view.getChildAt(i))
+ if (match != null) {
+ return match
}
- })
- }
-
- fun onOriginalDialogSizeChanged() {
- // The dialog is the single child of the root.
- if (hostDialogRoot.childCount != 1) {
- return
}
- val dialogView = hostDialogRoot.getChildAt(0)
- val layoutParams = dialogView.layoutParams as? FrameLayout.LayoutParams ?: return
- layoutParams.width = originalDialog.window.attributes.width
- layoutParams.height = originalDialog.window.attributes.height
- dialogView.layoutParams = layoutParams
+ return null
}
private fun maybeStartLaunchAnimation() {
@@ -542,7 +445,7 @@
}
// Show the background dim.
- hostDialog.window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ dialog.window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
startAnimation(
isLaunching = true,
@@ -564,22 +467,23 @@
// dismiss was called during the animation, dismiss again now to actually
// dismiss.
if (dismissRequested) {
- hostDialog.dismiss()
+ dialog.dismiss()
}
// If necessary, we animate the dialog background when its bounds change. We do it
// at the end of the launch animation, because the lauch animation already correctly
// handles bounds changes.
if (backgroundLayoutListener != null) {
- dialogContentParent.addOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground!!
+ .addOnLayoutChangeListener(backgroundLayoutListener)
}
}
)
}
- private fun onHostDialogDismissed(actualDismiss: () -> Unit) {
+ private fun onDialogDismissed() {
if (Looper.myLooper() != Looper.getMainLooper()) {
- context.mainExecutor.execute { onHostDialogDismissed(actualDismiss) }
+ context.mainExecutor.execute { onDialogDismissed() }
return
}
@@ -594,23 +498,29 @@
}
isDismissing = true
- hideDialogIntoView { instantDismiss: Boolean ->
- if (instantDismiss) {
- originalDialog.hide()
- hostDialog.hide()
+ hideDialogIntoView { animationRan: Boolean ->
+ if (animationRan) {
+ // Instantly dismiss the dialog if we ran the animation into view. If it was
+ // skipped, dismiss() will run the window animation (which fades out the dialog).
+ dialog.hide()
}
- originalDialog.dismiss()
- actualDismiss()
+ dialog.setDismissOverride(null)
+ dialog.dismiss()
}
}
/**
- * Hide the dialog into the touch surface and call [dismissDialogs] when the animation is done
- * (passing instantDismiss=true) or if it's skipped (passing instantDismiss=false) to actually
- * dismiss the dialogs.
+ * Hide the dialog into the touch surface and call [onAnimationFinished] when the animation is
+ * done (passing animationRan=true) or if it's skipped (passing animationRan=false) to actually
+ * dismiss the dialog.
*/
- private fun hideDialogIntoView(dismissDialogs: (Boolean) -> Unit) {
+ private fun hideDialogIntoView(onAnimationFinished: (Boolean) -> Unit) {
+ // Remove the layout change listener we have added to the DecorView earlier.
+ if (decorViewLayoutListener != null) {
+ decorView.removeOnLayoutChangeListener(decorViewLayoutListener)
+ }
+
if (!shouldAnimateDialogIntoView()) {
Log.i(TAG, "Skipping animation of dialog into the touch surface")
@@ -622,7 +532,7 @@
touchSurface.visibility = View.VISIBLE
}
- dismissDialogs(false /* instantDismiss */)
+ onAnimationFinished(false /* instantDismiss */)
onDialogDismissed(this@AnimatedDialog)
return
}
@@ -631,23 +541,25 @@
isLaunching = false,
onLaunchAnimationStart = {
// Remove the dim background as soon as we start the animation.
- hostDialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ dialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
},
onLaunchAnimationEnd = {
// Make sure we allow the touch surface to change its visibility again.
(touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
touchSurface.visibility = View.VISIBLE
- dialogContentParent.visibility = View.INVISIBLE
+ val dialogContentWithBackground = this.dialogContentWithBackground!!
+ dialogContentWithBackground.visibility = View.INVISIBLE
if (backgroundLayoutListener != null) {
- dialogContentParent.removeOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground
+ .removeOnLayoutChangeListener(backgroundLayoutListener)
}
// The animated ghost was just removed. We create a temporary ghost that will be
// removed only once we draw the touch surface, to avoid flickering that would
// happen when removing the ghost too early (before the touch surface is drawn).
- GhostView.addGhost(touchSurface, hostDialogRoot)
+ GhostView.addGhost(touchSurface, decorView)
touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
override fun onPreDraw(): Boolean {
@@ -656,7 +568,7 @@
// Now that the touch surface was drawn, we can remove the temporary ghost
// and instantly dismiss the dialog.
GhostView.removeGhost(touchSurface)
- dismissDialogs(true /* instantDismiss */)
+ onAnimationFinished(true /* instantDismiss */)
onDialogDismissed(this@AnimatedDialog)
return true
@@ -672,14 +584,14 @@
onLaunchAnimationStart: () -> Unit = {},
onLaunchAnimationEnd: () -> Unit = {}
) {
- // Create 2 ghost controllers to animate both the dialog and the touch surface in the host
+ // Create 2 ghost controllers to animate both the dialog and the touch surface in the
// dialog.
- val startView = if (isLaunching) touchSurface else dialogContentParent
- val endView = if (isLaunching) dialogContentParent else touchSurface
+ val startView = if (isLaunching) touchSurface else dialogContentWithBackground!!
+ val endView = if (isLaunching) dialogContentWithBackground!! else touchSurface
val startViewController = GhostedViewLaunchAnimatorController(startView)
val endViewController = GhostedViewLaunchAnimatorController(endView)
- startViewController.launchContainer = hostDialogRoot
- endViewController.launchContainer = hostDialogRoot
+ startViewController.launchContainer = decorView
+ endViewController.launchContainer = decorView
val endState = endViewController.createAnimatorState()
val controller = object : LaunchAnimator.Controller {
@@ -736,7 +648,15 @@
}
private fun shouldAnimateDialogIntoView(): Boolean {
- if (exitAnimationDisabled) {
+ // Don't animate if the dialog was previously hidden using hide() or if we disabled the exit
+ // animation.
+ if (exitAnimationDisabled || !dialog.isShowing) {
+ return false
+ }
+
+ // If we are dreaming, the dialog was probably closed because of that so we don't animate
+ // into the touchSurface.
+ if (dreamManager.isDreaming) {
return false
}
@@ -837,9 +757,9 @@
return touchSurface
}
parentAnimatedDialog.exitAnimationDisabled = true
- parentAnimatedDialog.originalDialog.hide()
+ parentAnimatedDialog.dialog.hide()
val view = parentAnimatedDialog.prepareForStackDismiss()
- parentAnimatedDialog.originalDialog.dismiss()
+ parentAnimatedDialog.dialog.dismiss()
// Make the touch surface invisible, so we end up animating to it when we actually
// dismiss the stack
view.visibility = View.INVISIBLE
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 3ccf5e4..5fec4cc 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -53,8 +53,12 @@
private val ghostedView: View,
/** The [InteractionJankMonitor.CujType] associated to this animation. */
- private val cujType: Int? = null
+ private val cujType: Int? = null,
+ private var interactionJankMonitor: InteractionJankMonitor? = null
) : ActivityLaunchAnimator.Controller {
+
+ constructor(view: View, type: Int) : this(view, type, null)
+
/** The container to which we will add the ghost view and expanding background. */
override var launchContainer = ghostedView.rootView as ViewGroup
private val launchContainerOverlay: ViewGroupOverlay
@@ -170,7 +174,7 @@
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
- cujType?.let { InteractionJankMonitor.getInstance().begin(ghostedView, it) }
+ cujType?.let { interactionJankMonitor?.begin(ghostedView, it) }
}
override fun onLaunchAnimationProgress(
@@ -251,7 +255,7 @@
return
}
- cujType?.let { InteractionJankMonitor.getInstance().end(it) }
+ cujType?.let { interactionJankMonitor?.end(it) }
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
diff --git a/packages/SystemUI/docs/dialogs.md b/packages/SystemUI/docs/dialogs.md
new file mode 100644
index 0000000..e5b4edd
--- /dev/null
+++ b/packages/SystemUI/docs/dialogs.md
@@ -0,0 +1,79 @@
+# Dialogs in SystemUI
+
+### Creating a dialog
+
+### Styling
+
+In order to have uniform styling in dialogs, use [SystemUIDialog][1] with its default theme.
+If not possible, use [AlertDialog][2] with the SystemUI theme `R.style.Theme_SystemUI_Dialog`.
+If needed, consider extending this theme instead of creating a new one.
+
+### Setting the internal elements
+
+The internal elements of the dialog are laid out using the following resources:
+
+* [@layout/alert_dialog_systemui][3]
+* [@layout/alert_dialog_title_systemui][4]
+* [@layout/alert_dialog_button_bar_systemui][5]
+
+Use the default components of the layout by calling the appropriate setters (in the dialog or
+[AlertDialog.Builder][2]). The supported styled setters are:
+
+* `setIcon`: tinted using `attr/colorAccentPrimaryVariant`.
+* `setTitle`: this will use `R.style.TextAppearance_Dialog_Title`.
+* `setMessage`: this will use `R.style.TextAppearance_Dialog_Body_Message`.
+* `SystemUIDialog.set<Positive|Negative|Neutral>Button` or `AlertDialog.setButton`: this will use
+ the following styles for buttons.
+ * `R.style.Widget_Dialog_Button` for the positive button.
+ * `R.style.Widget_Dialog_Button_BorderButton` for the negative and neutral buttons.
+
+ If needed to use the same style for all three buttons, the style attributes
+ `?android:attr/buttonBar<Positive|NegativeNeutral>Button` can be overriden in a theme that extends
+ from `R.style.Theme_SystemUI_Dialog`.
+* `setView`: to set a custom view in the dialog instead of using `setMessage`.
+
+Using `setContentView` is discouraged as this replaces the content completely.
+
+All these calls should be made before `Dialog#create` or `Dialog#show` (which internally calls
+`create`) are called, as that's when the content is installed.
+
+## Showing the dialog
+
+When showing a dialog triggered by clicking on a `View`, you should use [DialogLaunchAnimator][6] to
+nicely animate the dialog from/to that `View`, instead of calling `Dialog.show`.
+
+This animator provides the following methods:
+
+* `showFromView`: animates the dialog show from a view , and the dialog dismissal/cancel/hide to the
+ same view.
+* `showFromDialog`: animates the dialog show from a currently showing dialog, and the dialog
+ dismissal/cancel/hide back to that dialog. The originating dialog must have been shown using
+ `DialogLaunchAnimator`.
+* `dismissStack`: dismisses a stack of dialogs that were launched using `showFromDialog` animating
+ the top-most dialog back into the view that was used in the initial `showFromView`.
+
+## Example
+
+Here's a short code snippet with an example on how to use the guidelines.
+
+```kotlin
+val dialog = SystemUIDialog(context).apply {
+ setIcon(R.drawable.my_icon)
+ setTitle(context.getString(R.string.title))
+ setMessage(context.getString(R.string.message))
+ // Alternatively to setMessage:
+ // val content = LayoutManager.from(context).inflate(R.layout.content, null)
+ // setView(content)
+ setPositiveButton(R.string.positive_button_text, ::onPositiveButton)
+ setNegativeButton(R.string.negative_button_text, ::onNegativeButton)
+ setNeutralButton(R.string.neutral_button_text, ::onNeutralButton)
+}
+dialogLaunchAnimator.showFromView(dialog, view)
+```
+
+[1]: /packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+[2]: /core/java/android/app/AlertDialog.java
+[3]: /packages/SystemUI/res/layout/alert_dialog_systemui.xml
+[4]: /packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
+[5]: /packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
+[6]: /packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
\ No newline at end of file
diff --git a/packages/SystemUI/docs/keyguard/bouncer.md b/packages/SystemUI/docs/keyguard/bouncer.md
index 51f8516..4bfe734 100644
--- a/packages/SystemUI/docs/keyguard/bouncer.md
+++ b/packages/SystemUI/docs/keyguard/bouncer.md
@@ -2,15 +2,24 @@
[KeyguardBouncer][1] is the component responsible for displaying the security method set by the user (password, PIN, pattern) as well as SIM-related security methods, allowing the user to unlock the device or SIM.
+## Supported States
+
+1. Phone, portrait mode - The default and typically only way to view the bouncer. Screen cannot rotate.
+1. Phone, landscape - Can only get into this state via lockscreen activities. Launch camera, rotate to landscape, tap lock icon is one example.
+1. Foldables - Both landscape and portrait are supported. In landscape, the bouncer can appear on either of the hinge and can be dragged to the other side. Also refered to as "OneHandedMode in [KeyguardSecurityContainerController][3]
+1. Tablets - The bouncer is supplemented with user icons and a multi-user switcher, when available.
+
## Components
The bouncer contains a hierarchy of controllers/views to render the user's security method and to manage the authentication attempts.
1. [KeyguardBouncer][1] - Entrypoint for managing the bouncer visibility.
1. [KeyguardHostViewController][2] - Intercepts media keys. Can most likely be merged with the next item.
- 1. [KeyguardSecurityContainerController][3] - Manages unlock attempt responses, one-handed use
+ 1. [KeyguardSecurityContainerController][3] - Manages unlock attempt responses, determines the correct security view layout, which may include a user switcher or enable one-handed use.
1. [KeyguardSecurityViewFlipperController][4] - Based upon the [KeyguardSecurityModel#SecurityMode][5], will instantiate the required view and controller. PIN, Pattern, etc.
+Fun fact: Naming comes from the concept of a bouncer at a bar or nightclub, who prevent troublemakers from entering or eject them from the premises.
+
[1]: /frameworks/base/packages/SystemUI/com/android/systemui/statusbar/phone/KeyguardBouncer
[2]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardHostViewController
[3]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardSecurityContainerController
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 07c2ac4..3517eba 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -36,4 +36,42 @@
-keep class com.android.systemui.dagger.GlobalRootComponent { *; }
-keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; }
-keep class com.android.systemui.dagger.Dagger** { *; }
--keep class com.android.systemui.tv.Dagger** { *; }
\ No newline at end of file
+-keep class com.android.systemui.tv.Dagger** { *; }
+
+# Removes runtime checks added through Kotlin to JVM code genereration to
+# avoid linear growth as more Kotlin code is converted / added to the codebase.
+# These checks are generally applied to Java platform types (values returned
+# from Java code that don't have nullness annotations), but we remove them to
+# avoid code size increases.
+#
+# See also https://kotlinlang.org/docs/reference/java-interop.html
+#
+# TODO(b/199941987): Consider standardizing these rules in a central place as
+# Kotlin gains adoption with other platform targets.
+-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
+ # Remove check for method parameters being null
+ static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
+
+ # When a Java platform type is returned and passed to Kotlin NonNull method,
+ # remove the null check
+ static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
+ static void checkNotNullExpressionValue(java.lang.Object, java.lang.String);
+
+ # Remove check that final value returned from method is null, if passing
+ # back Java platform type.
+ static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
+ static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String);
+
+ # Null check for accessing a field from a parent class written in Java.
+ static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
+ static void checkFieldIsNotNull(java.lang.Object, java.lang.String);
+
+ # Removes code generated from !! operator which converts Nullable type to
+ # NonNull type. These would throw an NPE immediate after on access.
+ static void checkNotNull(java.lang.Object, java.lang.String);
+ static void checkNotNullParameter(java.lang.Object, java.lang.String);
+
+ # Removes lateinit var check being used before being set. Check is applied
+ # on every field access without this.
+ static void throwUninitializedPropertyAccessException(java.lang.String);
+}
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
index e09bf7e..625ce1f 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
@@ -16,5 +16,7 @@
-->
<resources>
+ <!-- Allows PIN/Pattern to be drawn on one side of a display, and for the user to
+ switch sides -->
<bool name="can_use_one_handed_bouncer">true</bool>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
index e09bf7e..4daa648 100644
--- a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
+++ b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
@@ -16,5 +16,11 @@
-->
<resources>
+ <!-- Allows PIN/Pattern to be drawn on one side of a display, and for the user to
+ switch sides -->
<bool name="can_use_one_handed_bouncer">true</bool>
+
+ <!-- Will display the bouncer on one side of the display, and the current user icon and
+ user switcher on the other side -->
+ <bool name="bouncer_display_user_switcher">true</bool>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 30f3c83..dfb0c81 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -86,9 +86,9 @@
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత నమూనాను గీయాలి"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"డివైజ్ను పునఃప్రారంభించిన తర్వాత పిన్ నమోదు చేయాలి"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత పాస్వర్డ్ను నమోదు చేయాలి"</string>
- <string name="kg_prompt_reason_timeout_pattern" msgid="9170360502528959889">"అదనపు భద్రత కోసం నమూనాని గీయాలి"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="5945186097160029201">"అదనపు భద్రత కోసం పిన్ నమోదు చేయాలి"</string>
- <string name="kg_prompt_reason_timeout_password" msgid="2258263949430384278">"అదనపు భద్రత కోసం పాస్వర్డ్ని నమోదు చేయాలి"</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="9170360502528959889">"అదనపు సెక్యూరిటీ కోసం ఆకృతి అవసరం"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="5945186097160029201">"అదనపు సెక్యూరిటీ కోసం పిన్ ఎంటర్ చేయాలి"</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="2258263949430384278">"అదనపు సెక్యూరిటీ కోసం పాస్వర్డ్ను ఎంటర్ చేయాలి"</string>
<string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"పరికరం నిర్వాహకుల ద్వారా లాక్ చేయబడింది"</string>
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"పరికరం మాన్యువల్గా లాక్ చేయబడింది"</string>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"గుర్తించలేదు"</string>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 6176f7c..6194aa0 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,5 +22,11 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
+ <!-- Allows PIN/Pattern to be drawn on one side of a display, and for the user to
+ switch sides -->
<bool name="can_use_one_handed_bouncer">false</bool>
+ <!-- Will display the bouncer on one side of the display, and the current user icon and
+ user switcher on the other side -->
+ <bool name="bouncer_display_user_switcher">false</bool>
+
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_list.xml b/packages/SystemUI/res/drawable/ic_list.xml
new file mode 100644
index 0000000..7ef5299
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_list.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+
+<!-- Remove when Fgs manager tile is removed -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M2,4h4v4h-4z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M8,4h14v4h-14z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M2,10h4v4h-4z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M8,10h14v4h-14z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M2,16h4v4h-4z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M8,16h14v4h-14z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
new file mode 100644
index 0000000..3a228d5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
@@ -0,0 +1,37 @@
+<?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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape>
+ <corners android:radius="28dp" />
+ <solid android:color="@android:color/transparent" />
+ <size
+ android:height="64dp"/>
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape>
+ <corners
+ android:radius="28dp"/>
+ <size
+ android:height="64dp"/>
+ <solid android:color="@*android:color/system_accent1_200" />
+ </shape>
+ </clip>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_solid_button_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_solid_button_background.xml
new file mode 100644
index 0000000..86f8b42
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_dialog_solid_button_background.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ 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.
+ -->
+<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="@android:color/transparent"
+ android:width="1dp"/>
+ <corners android:radius="20dp"/>
+ <padding
+ android:left="@dimen/media_output_dialog_button_padding_horizontal"
+ android:right="@dimen/media_output_dialog_button_padding_horizontal"
+ android:top="@dimen/media_output_dialog_button_padding_vertical"
+ android:bottom="@dimen/media_output_dialog_button_padding_vertical" />
+ <solid android:color="?androidprv:attr/colorAccentPrimaryVariant" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/packages/SystemUI/res/drawable/media_output_item_background.xml
similarity index 67%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to packages/SystemUI/res/drawable/media_output_item_background.xml
index 9f44aca..8c23659 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/packages/SystemUI/res/drawable/media_output_item_background.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -14,7 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners
+ android:radius="16dp"/>
+ <solid android:color="?androidprv:attr/colorAccentSecondary" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/packages/SystemUI/res/drawable/media_output_item_background_active.xml
similarity index 67%
copy from packages/SystemUI/res/color/prv_text_color_on_accent.xml
copy to packages/SystemUI/res/drawable/media_output_item_background_active.xml
index 9f44aca..09dee95 100644
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ b/packages/SystemUI/res/drawable/media_output_item_background_active.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -14,7 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners
+ android:radius="50dp"/>
+ <solid android:color="?androidprv:attr/colorAccentSecondary" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/media_output_status_check.xml b/packages/SystemUI/res/drawable/media_output_status_check.xml
new file mode 100644
index 0000000..4e17e48
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_status_check.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="?androidprv:attr/colorAccentPrimaryVariant"
+ android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/media_output_status_failed.xml b/packages/SystemUI/res/drawable/media_output_status_failed.xml
new file mode 100644
index 0000000..81fb92c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_status_failed.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="?androidprv:attr/colorAccentPrimaryVariant"
+ android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
index 1a128df..14cb1de 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
@@ -16,8 +16,8 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:insetTop="@dimen/qs_dialog_button_vertical_inset"
- android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+ android:insetTop="@dimen/dialog_button_vertical_inset"
+ android:insetBottom="@dimen/dialog_button_vertical_inset">
<ripple android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
@@ -29,10 +29,10 @@
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
<solid android:color="?androidprv:attr/colorAccentPrimary"/>
- <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
- android:top="@dimen/qs_dialog_button_vertical_padding"
- android:right="@dimen/qs_dialog_button_horizontal_padding"
- android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+ <padding android:left="@dimen/dialog_button_horizontal_padding"
+ android:top="@dimen/dialog_button_vertical_padding"
+ android:right="@dimen/dialog_button_horizontal_padding"
+ android:bottom="@dimen/dialog_button_vertical_padding"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index 467c20f..665b456 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -16,8 +16,8 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:insetTop="@dimen/qs_dialog_button_vertical_inset"
- android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+ android:insetTop="@dimen/dialog_button_vertical_inset"
+ android:insetBottom="@dimen/dialog_button_vertical_inset">
<ripple android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
@@ -32,10 +32,10 @@
<stroke android:color="?androidprv:attr/colorAccentPrimary"
android:width="1dp"
/>
- <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
- android:top="@dimen/qs_dialog_button_vertical_padding"
- android:right="@dimen/qs_dialog_button_horizontal_padding"
- android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+ <padding android:left="@dimen/dialog_button_horizontal_padding"
+ android:top="@dimen/dialog_button_vertical_padding"
+ android:right="@dimen/dialog_button_horizontal_padding"
+ android:bottom="@dimen/dialog_button_vertical_padding"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index 0dec981..991dc63e 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -35,7 +35,7 @@
android:id="@+id/volume_number"
android:layout_width="@dimen/tv_volume_dialog_bubble_size"
android:layout_height="@dimen/tv_volume_dialog_bubble_size"
- android:maxLength="2"
+ android:maxLength="3"
android:gravity="center"
android:fontFeatureSettings="tnum"
android:background="@drawable/tv_volume_dialog_circle"
diff --git a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
new file mode 100644
index 0000000..a3e289a
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
@@ -0,0 +1,63 @@
+<?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.
+ -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@*android:id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollIndicators="top|bottom"
+ android:fillViewport="true"
+ android:paddingTop="@dimen/dialog_button_bar_top_padding"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ android:paddingBottom="@dimen/dialog_bottom_padding"
+ style="?android:attr/buttonBarStyle">
+ <com.android.internal.widget.ButtonBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale"
+ android:orientation="horizontal"
+ android:gravity="bottom">
+
+ <Button
+ android:id="@android:id/button3"
+ style="?android:attr/buttonBarNeutralButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Space
+ android:id="@*android:id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+ <Button
+ android:id="@android:id/button2"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@android:id/button1"
+ style="?android:attr/buttonBarPositiveButtonStyle"
+ android:layout_marginStart="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </com.android.internal.widget.ButtonBarLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
new file mode 100644
index 0000000..f280cbd
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
@@ -0,0 +1,91 @@
+<?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.internal.widget.AlertDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@*android:id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|top"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/dialog_top_padding"
+ >
+
+ <include layout="@layout/alert_dialog_title_systemui" />
+
+ <FrameLayout
+ android:id="@*android:id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ >
+
+ <ScrollView
+ android:id="@*android:id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Space
+ android:id="@*android:id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+
+ <TextView
+ android:id="@*android:id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.Dialog.Body.Message" />
+
+ <Space
+ android:id="@*android:id/textSpacerNoButtons"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="6dp" />
+ </LinearLayout>
+ </ScrollView>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@*android:id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ >
+
+ <FrameLayout
+ android:id="@*android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/alert_dialog_button_bar_systemui" />
+
+</com.android.internal.widget.AlertDialogLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
new file mode 100644
index 0000000..88f13b4
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
@@ -0,0 +1,62 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@*android:id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+>
+
+ <!-- If the client uses a customTitle, it will be added here. -->
+
+ <LinearLayout
+ android:id="@*android:id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_horizontal|top">
+
+ <ImageView
+ android:id="@*android:id/icon"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginBottom="16dp"
+ android:scaleType="fitCenter"
+ android:src="@null"
+ android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+ />
+
+ <TextView
+ android:id="@*android:id/alertTitle"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ style="@style/TextAppearance.Dialog.Title" />
+ </LinearLayout>
+
+ <Space
+ android:id="@*android:id/titleDividerNoCustom"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/fgs_manager_app_item.xml b/packages/SystemUI/res/layout/fgs_manager_app_item.xml
new file mode 100644
index 0000000..d034f4e
--- /dev/null
+++ b/packages/SystemUI/res/layout/fgs_manager_app_item.xml
@@ -0,0 +1,57 @@
+<?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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="32dp"
+ android:orientation="horizontal"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/fgs_manager_app_item_icon"
+ android:layout_width="28dp"
+ android:layout_height="28dp"
+ android:layout_marginRight="12dp" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/fgs_manager_app_item_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start"
+ style="@style/TextAppearance.Dialog.Body" />
+ <TextView
+ android:id="@+id/fgs_manager_app_item_duration"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start"
+ style="@style/FgsManagerAppDuration" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/fgs_manager_app_item_stop_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/fgs_manager_app_item_stop_button_label"
+ android:layout_marginLeft="12dp"
+ style="?android:attr/buttonBarNeutralButtonStyle" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 86e2661..4a5b637 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -156,6 +156,14 @@
style="@style/InternetDialog.NetworkSummary"/>
</LinearLayout>
+ <View
+ android:id="@+id/mobile_toggle_divider"
+ android:layout_width="1dp"
+ android:layout_height="28dp"
+ android:layout_marginEnd="16dp"
+ android:layout_gravity="center_vertical"
+ android:background="?android:attr/textColorSecondary"/>
+
<FrameLayout
android:layout_width="@dimen/settingslib_switch_track_width"
android:layout_height="48dp"
@@ -177,7 +185,7 @@
<LinearLayout
android:id="@+id/turn_on_wifi_layout"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:gravity="center"
android:clickable="false"
android:focusable="false">
@@ -219,7 +227,7 @@
<LinearLayout
android:id="@+id/wifi_connected_layout"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:paddingStart="20dp"
android:paddingEnd="24dp"
android:background="@drawable/settingslib_switch_bar_bg_on"
@@ -241,7 +249,7 @@
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:layout_marginEnd="30dp"
android:layout_weight="1"
android:gravity="start|center_vertical">
@@ -367,8 +375,9 @@
android:id="@+id/done_layout"
android:layout_width="67dp"
android:layout_height="48dp"
+ android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
- android:layout_marginBottom="40dp"
+ android:layout_marginBottom="34dp"
android:layout_gravity="end|center_vertical"
android:clickable="true"
android:focusable="true">
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
index 868331e..f6a2136 100644
--- a/packages/SystemUI/res/layout/internet_list_item.xml
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -25,7 +25,7 @@
<LinearLayout
android:id="@+id/wifi_list"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:paddingStart="20dp"
android:paddingEnd="24dp">
<FrameLayout
@@ -45,7 +45,7 @@
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:layout_marginEnd="30dp"
android:layout_weight="1"
android:gravity="start|center_vertical">
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index cfba83b..8f8993f 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -17,7 +17,6 @@
<com.android.systemui.statusbar.phone.KeyguardBottomAreaView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/keyguard_bottom_area"
android:layout_height="match_parent"
android:layout_width="match_parent"
@@ -128,7 +127,8 @@
android:layout_height="match_parent">
<include layout="@layout/keyguard_bottom_area_overlay" />
-
</FrameLayout>
+ <include layout="@layout/ambient_indication"
+ android:id="@+id/ambient_indication_container" />
</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index a64ef3e..4e2b4a6 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -24,24 +24,30 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="96dp"
+ android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:paddingStart="16dp"
+ android:paddingTop="16dp"
+ android:paddingEnd="16dp"
+ android:paddingBottom="24dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/header_icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
+ android:layout_width="72dp"
+ android:layout_height="72dp"
android:importantForAccessibility="no"/>
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="16dp"
- android:paddingTop="20dp"
- android:paddingBottom="24dp"
- android:paddingEnd="24dp"
+ android:layout_height="wrap_content"
+ android:paddingStart="12dp"
android:orientation="vertical">
+ <ImageView
+ android:id="@+id/app_source_icon"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:gravity="center_vertical"
+ android:importantForAccessibility="no"/>
<TextView
android:id="@+id/header_title"
android:layout_width="wrap_content"
@@ -51,7 +57,7 @@
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textSize="20sp"/>
+ android:textSize="16sp"/>
<TextView
android:id="@+id/header_subtitle"
android:layout_width="wrap_content"
@@ -59,17 +65,12 @@
android:gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
- android:textColor="?android:attr/textColorTertiary"
+ android:textColor="?android:attr/textColorSecondary"
android:fontFamily="roboto-regular"
- android:textSize="16sp"/>
+ android:textSize="14sp"/>
</LinearLayout>
</LinearLayout>
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?android:attr/listDivider"/>
-
<LinearLayout
android:id="@+id/device_list"
android:layout_width="match_parent"
@@ -88,10 +89,10 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:layout_marginStart="24dp"
- android:layout_marginBottom="18dp"
- android:layout_marginEnd="24dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginBottom="24dp"
+ android:layout_marginEnd="16dp"
android:orientation="horizontal">
<Button
@@ -110,7 +111,7 @@
<Button
android:id="@+id/done"
- style="@style/MediaOutputRoundedOutlinedButton"
+ style="@style/MediaOutputRoundedButton"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:minWidth="0dp"
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index a5a7efa..2f5f8d6 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -17,26 +17,43 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/device_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="88dp"
- android:paddingTop="24dp"
- android:paddingBottom="16dp"
- android:paddingStart="24dp"
- android:paddingEnd="8dp">
+ android:layout_height="64dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:layout_marginBottom="12dp">
+ <FrameLayout
+ android:id="@+id/item_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/media_output_item_background"
+ android:layout_gravity="center_vertical|start">
+ <SeekBar
+ android:id="@+id/volume_seekbar"
+ android:splitTrack="false"
+ android:visibility="gone"
+ android:paddingStart="0dp"
+ android:paddingEnd="0dp"
+ android:progressDrawable="@drawable/media_output_dialog_seekbar_background"
+ android:thumb="@null"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ </FrameLayout>
<FrameLayout
- android:layout_width="48dp"
- android:layout_height="48dp"
+ android:layout_width="56dp"
+ android:layout_height="64dp"
android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/title_icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
android:layout_gravity="center"/>
</FrameLayout>
@@ -45,55 +62,46 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:layout_marginStart="64dp"
+ android:layout_marginStart="56dp"
android:ellipsize="end"
android:maxLines="1"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
android:textSize="16sp"/>
<RelativeLayout
android:id="@+id/two_line_layout"
android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|start"
android:layout_height="48dp"
- android:layout_marginStart="48dp">
+ android:layout_marginStart="56dp">
<TextView
android:id="@+id/two_line_title"
+ android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="48dp"
android:ellipsize="end"
android:maxLines="1"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
android:textSize="16sp"/>
<TextView
android:id="@+id/subtitle"
+ android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="15dp"
android:layout_marginTop="4dp"
android:layout_alignParentBottom="true"
android:ellipsize="end"
android:maxLines="1"
- android:textColor="?android:attr/textColorSecondary"
+ android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
android:textSize="14sp"
android:fontFamily="roboto-regular"
android:visibility="gone"/>
- <SeekBar
- android:id="@+id/volume_seekbar"
- android:layout_marginTop="16dp"
- android:layout_marginEnd="8dp"
- style="@*android:style/Widget.DeviceDefault.SeekBar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"/>
<ImageView
android:id="@+id/add_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="right"
- android:layout_marginEnd="24dp"
+ android:layout_marginEnd="16dp"
android:layout_alignParentRight="true"
android:src="@drawable/ic_add"
android:tint="?android:attr/colorAccent"
@@ -103,30 +111,33 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="right"
- android:layout_marginEnd="24dp"
+ android:layout_marginEnd="16dp"
android:layout_alignParentRight="true"
android:button="@drawable/ic_check_box"
android:visibility="gone"
- />
+ />
</RelativeLayout>
<ProgressBar
android:id="@+id/volume_indeterminate_progress"
- style="@*android:style/Widget.Material.ProgressBar.Horizontal"
- android:layout_width="258dp"
- android:layout_height="18dp"
- android:layout_marginStart="64dp"
- android:layout_marginTop="28dp"
+ style="?android:attr/progressBarStyleSmallTitle"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="16dp"
android:indeterminate="true"
+ android:layout_gravity="right|center"
android:indeterminateOnly="true"
android:visibility="gone"/>
- </FrameLayout>
- <View
- android:id="@+id/bottom_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_gravity="bottom"
- android:background="?android:attr/listDivider"
- android:visibility="gone"/>
+ <ImageView
+ android:id="@+id/media_output_item_status"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="16dp"
+ android:indeterminate="true"
+ android:layout_gravity="right|center"
+ android:indeterminateOnly="true"
+ android:importantForAccessibility="no"
+ android:visibility="gone"/>
+ </FrameLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/privacy_dialog.xml b/packages/SystemUI/res/layout/privacy_dialog.xml
index ee4530c..9368a6a 100644
--- a/packages/SystemUI/res/layout/privacy_dialog.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog.xml
@@ -22,10 +22,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/ongoing_appops_dialog_side_margins"
android:layout_marginEnd="@dimen/ongoing_appops_dialog_side_margins"
- android:layout_marginTop="8dp"
android:orientation="vertical"
android:paddingBottom="8dp"
- android:paddingTop="12dp"
+ android:paddingTop="8dp"
android:paddingHorizontal="@dimen/ongoing_appops_dialog_side_padding"
android:background="@drawable/qs_dialog_bg"
/>
diff --git a/packages/SystemUI/res/layout/qs_user_detail.xml b/packages/SystemUI/res/layout/qs_user_detail.xml
index 91d3a53..1aec296 100644
--- a/packages/SystemUI/res/layout/qs_user_detail.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail.xml
@@ -22,6 +22,6 @@
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- sysui:verticalSpacing="4dp"
+ sysui:verticalSpacing="20dp"
sysui:horizontalSpacing="4dp"
style="@style/UserDetailView" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index cc6c5d3..91b11fc 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -24,8 +24,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="top|center_horizontal"
- android:paddingTop="16dp"
- android:minHeight="112dp"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
diff --git a/packages/SystemUI/res/layout/qs_user_dialog_content.xml b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
index 9495ee6..355df2c 100644
--- a/packages/SystemUI/res/layout/qs_user_dialog_content.xml
+++ b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
@@ -15,75 +15,19 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<androidx.constraintlayout.widget.ConstraintLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="24dp"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
->
- <TextView
- android:id="@+id/title"
- android:layout_height="wrap_content"
- android:layout_width="0dp"
- android:textAlignment="center"
- android:text="@string/qs_user_switch_dialog_title"
- android:textAppearance="@style/TextAppearance.QSDialog.Title"
- android:layout_marginBottom="32dp"
- sysui:layout_constraintTop_toTopOf="parent"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toEndOf="parent"
- sysui:layout_constraintBottom_toTopOf="@id/grid"
- />
-
+ >
<com.android.systemui.qs.PseudoGridView
- android:id="@+id/grid"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="28dp"
- sysui:verticalSpacing="4dp"
- sysui:horizontalSpacing="4dp"
- sysui:fixedChildWidth="80dp"
- sysui:layout_constraintTop_toBottomOf="@id/title"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toEndOf="parent"
- sysui:layout_constraintBottom_toTopOf="@id/barrier"
- />
-
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/barrier"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- sysui:barrierDirection="top"
- sysui:constraint_referenced_ids="settings,done"
+ android:id="@+id/grid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ sysui:verticalSpacing="20dp"
+ sysui:horizontalSpacing="4dp"
+ sysui:fixedChildWidth="80dp"
/>
-
- <Button
- android:id="@+id/settings"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:text="@string/quick_settings_more_user_settings"
- sysui:layout_constraintTop_toBottomOf="@id/barrier"
- sysui:layout_constraintBottom_toBottomOf="parent"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toStartOf="@id/done"
- sysui:layout_constraintHorizontal_chainStyle="spread_inside"
- style="@style/Widget.QSDialog.Button.BorderButton"
- />
-
- <Button
- android:id="@+id/done"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:text="@string/quick_settings_done"
- sysui:layout_constraintTop_toBottomOf="@id/barrier"
- sysui:layout_constraintBottom_toBottomOf="parent"
- sysui:layout_constraintStart_toEndOf="@id/settings"
- sysui:layout_constraintEnd_toEndOf="parent"
- style="@style/Widget.QSDialog.Button"
- />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/split_shade_header.xml b/packages/SystemUI/res/layout/split_shade_header.xml
index f2c5b7b..b6e96ce 100644
--- a/packages/SystemUI/res/layout/split_shade_header.xml
+++ b/packages/SystemUI/res/layout/split_shade_header.xml
@@ -83,6 +83,17 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
systemui:textAppearance="@style/TextAppearance.QS.Status" />
+ <FrameLayout
+ android:id="@+id/privacy_container"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:minHeight="48dp"
+ android:layout_weight="1"
+ android:paddingStart="16dp">
+
+ <include layout="@layout/ongoing_privacy_chip" />
+
+ </FrameLayout>
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index b4c9a93..2290964 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -128,9 +128,6 @@
systemui:layout_constraintEnd_toEndOf="parent"
/>
- <include layout="@layout/ambient_indication"
- android:id="@+id/ambient_indication_container" />
-
<include layout="@layout/photo_preview_overlay" />
<include
diff --git a/packages/SystemUI/res/layout/tile_service_request_dialog.xml b/packages/SystemUI/res/layout/tile_service_request_dialog.xml
index b431d44..3a8a69c 100644
--- a/packages/SystemUI/res/layout/tile_service_request_dialog.xml
+++ b/packages/SystemUI/res/layout/tile_service_request_dialog.xml
@@ -30,7 +30,6 @@
android:layout_marginBottom="16dp"
android:textDirection="locale"
android:textAlignment="viewStart"
- android:textAppearance="@style/TextAppearance.PrivacyDialog"
- android:lineHeight="20sp"
+ android:textAppearance="@style/TextAppearance.Dialog.Body"
/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index f2d27c4..f2754aa 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Foon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Stembystand"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Beursie"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kodeskandeerder"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Ontsluit"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Toestel is gesluit"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skandeer tans gesig"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skermopname"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Begin"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokkeer toestelmikrofoon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokkeer toestelkamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokkeer toestelkamera en mikrofoon?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontsluit om te gebruik"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kon nie jou kaarte kry nie; probeer later weer"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Sluitskerminstellings"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skandeer QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik om \'n QR-kode te skandeer"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Jy sal nie jou volgende wekker <xliff:g id="WHEN">%1$s</xliff:g> hoor nie"</string>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index c7e76180..a9e1a23 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Af"</item>
<item msgid="6866424167599381915">"Aan"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Onbeskikbaar"</item>
+ <item msgid="3301403109049256043">"Af"</item>
+ <item msgid="8878684975184010135">"Aan"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Onbeskikbaar"</item>
<item msgid="2710157085538036590">"Af"</item>
<item msgid="7809470840976856149">"Aan"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Onbeskikbaar"</item>
+ <item msgid="146088982397753810">"Af"</item>
+ <item msgid="460891964396502657">"Aan"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index b71cf7a..e463894 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ስልክ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"የድምጽ እርዳታ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"የኪስ ቦርሳ"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"የQR ኮድ መቃኛ"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ክፈት"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"መሣሪያ ተቆልፏል"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"የቅኝት ፊት"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"የማያ ቀረጻ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ጀምር"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"አቁም"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"የአንድ እጅ ሁነታ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"የመሣሪያ ማይክሮፎን እገዳ ይነሳ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"የመሣሪያ ካሜራ እገዳ ይነሳ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"የመሣሪያ ካሜራ እና ማይክሮፎን እገዳ ይነሳ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ለማየት ይክፈቱ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"የእርስዎን ካርዶች ማግኘት ላይ ችግር ነበር፣ እባክዎ ቆይተው እንደገና ይሞክሩ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"የገጽ መቆለፊያ ቅንብሮች"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ቃኝ"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ኮድን ለመቃኘት ጠቅ ያድርጉ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"የስራ መገለጫ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"የአውሮፕላን ሁነታ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"የእርስዎን ቀጣይ ማንቂያ <xliff:g id="WHEN">%1$s</xliff:g> አይሰሙም"</string>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index e5be860..2b0062a 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ጠፍቷል"</item>
<item msgid="6866424167599381915">"በርቷል"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"አይገኝም"</item>
+ <item msgid="3301403109049256043">"አጥፋ"</item>
+ <item msgid="8878684975184010135">"አብራ"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"አይገኝም"</item>
<item msgid="2710157085538036590">"ጠፍቷል"</item>
<item msgid="7809470840976856149">"በርቷል"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"አይገኝም"</item>
+ <item msgid="146088982397753810">"አጥፋ"</item>
+ <item msgid="460891964396502657">"አብራ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 4141969..6f73bed 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"الهاتف"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"المساعد الصوتي"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"المحفظة"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"الماسح الضوئي لرمز الاستجابة السريعة"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"فتح القفل"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"الجهاز مُقفل."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"مسح الوجه"</string>
@@ -301,6 +300,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"تسجيل الشاشة"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"بدء"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"إيقاف"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"هل تريد إزالة حظر ميكروفون الجهاز؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"هل تريد إزالة حظر كاميرا الجهاز؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"هل تريد إزالة حظر الكاميرا والميكروفون؟"</string>
@@ -478,10 +478,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"فتح القفل للاستخدام"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"حدثت مشكلة أثناء الحصول على البطاقات، يُرجى إعادة المحاولة لاحقًا."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"إعدادات شاشة القفل"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"مسح رمز الاستجابة السريعة ضوئيًا"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"انقر لمسح رمز الاستجابة السريعة ضوئيًا."</string>
<string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index e2b8632..4facf293d 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"الميزة غير مفعّلة"</item>
<item msgid="6866424167599381915">"الميزة مفعّلة"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"غير متوفّر"</item>
+ <item msgid="3301403109049256043">"غير مفعّل"</item>
+ <item msgid="8878684975184010135">"مفعّل"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"الميزة غير متاحة"</item>
<item msgid="2710157085538036590">"الميزة غير مفعّلة"</item>
<item msgid="7809470840976856149">"الميزة مفعّلة"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"غير متاح"</item>
+ <item msgid="146088982397753810">"غير مفعّل"</item>
+ <item msgid="460891964396502657">"مفعّل"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 7c7d95b..679231f 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ফ\'ন"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"কণ্ঠধ্বনিৰে সহায়"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ৱালেট"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"কিউআৰ ক’ড স্কেনাৰ"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"আনলক কৰক"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ডিভাইচটো লক হৈ আছে"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"চেহেৰা স্কেন কৰি থকা হৈছে"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"স্ক্ৰীন ৰেকৰ্ড কৰা"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"আৰম্ভ কৰক"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"বন্ধ কৰক"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইচৰ মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইচৰ কেমেৰা অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইচৰ কেমেৰা আৰু মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যৱহাৰ কৰিবলৈ আনলক কৰক"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"আপোনাৰ কাৰ্ড লাভ কৰোঁতে এটা সমস্যা হৈছে, অনুগ্ৰহ কৰি পাছত পুনৰ চেষ্টা কৰক"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্ৰীনৰ ছেটিং"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"কিউআৰ স্কেন কৰক"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"এটা কিউআৰ ক’ড স্কেন কৰিবলৈ ক্লিক কৰক"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"এয়াৰপ্লেইন ম\'ড"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index 43957f4..69a2efc 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"অফ আছে"</item>
<item msgid="6866424167599381915">"অন কৰা আছে"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"উপলব্ধ নহয়"</item>
+ <item msgid="3301403109049256043">"অফ আছে"</item>
+ <item msgid="8878684975184010135">"অন আছে"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"উপলব্ধ নহয়"</item>
<item msgid="2710157085538036590">"অফ আছে"</item>
<item msgid="7809470840976856149">"অন কৰা আছে"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"উপলব্ধ নহয়"</item>
+ <item msgid="146088982397753810">"অফ"</item>
+ <item msgid="460891964396502657">"অন"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 8e27b0f..a7fe41d 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Səs Yardımçısı"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Pulqabı"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR Kodu Skaneri"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Kiliddən çıxarın"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Cihaz kilidlənib"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Üzün skan edilməsi"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran yazması"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Başlayın"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dayandırın"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bir əlli rejim"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonu blokdan çıxarılsın?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerası blokdan çıxarılsın?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası və mikrofonu blokdan çıxarılsın?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"İstifadə etmək üçün kiliddən çıxarın"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kartların əldə edilməsində problem oldu, sonra yenidən cəhd edin"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilid ekranı ayarları"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodunu skan edin"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodu skan etmək üçün tıklayın"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Təyyarə rejimi"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> zaman növbəti xəbərdarlığınızı eşitməyəcəksiniz"</string>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index 4baea08..d46175b 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Deaktiv"</item>
<item msgid="6866424167599381915">"Aktiv"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Əlçatan deyil"</item>
+ <item msgid="3301403109049256043">"Deaktiv"</item>
+ <item msgid="8878684975184010135">"Aktiv"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Əlçatan deyil"</item>
<item msgid="2710157085538036590">"Deaktiv"</item>
<item msgid="7809470840976856149">"Aktiv"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Əlçatmazdır"</item>
+ <item msgid="146088982397753810">"Deaktiv"</item>
+ <item msgid="460891964396502657">"Aktiv"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 6d5e75a..bebec60 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Novčanik"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skener QR koda"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Otključajte"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
@@ -295,6 +294,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Počnite"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednom rukom"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite da odblokirate mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite da odblokirate kameru uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite da odblokirate kameru i mikrofon uređaja?"</string>
@@ -469,10 +469,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključaj radi korišćenja"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema pri preuzimanju kartica. Probajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Podešavanja zaključanog ekrana"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR kôd"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da biste skenirali QR kôd"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim rada u avionu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sledeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -675,7 +673,7 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Neke funkcije su ograničene dok se telefon ne ohladi.\nDodirnite za više informacija"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon će automatski pokušati da se ohladi. I dalje ćete moći da koristite telefon, ali će sporije reagovati.\n\nKada se telefon ohladi, normalno će raditi."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pogledajte upozorenja"</string>
- <string name="high_temp_alarm_title" msgid="2359958549570161495">"Isključite punjač iz napajanja"</string>
+ <string name="high_temp_alarm_title" msgid="2359958549570161495">"Isključite punjač iz struje"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Došlo je do problema sa punjenjem ovog uređaja. Isključite adapter iz napajanja i budite pažljivi jer kabl može da bude topao."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Pogledajte upozorenja"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Leva prečica"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index 90b8cce..5d9edf5 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Isključeno"</item>
<item msgid="6866424167599381915">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nedostupno"</item>
+ <item msgid="3301403109049256043">"Isključeno"</item>
+ <item msgid="8878684975184010135">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nedostupno"</item>
<item msgid="2710157085538036590">"Isključeno"</item>
<item msgid="7809470840976856149">"Uključeno"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nedostupno"</item>
+ <item msgid="146088982397753810">"Isključeno"</item>
+ <item msgid="460891964396502657">"Uključeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index e98c498..98a4e22 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Тэлефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Галасавая дапамога"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Кашалёк"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Сканер QR-кодаў"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Разблакiраваць"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Прылада заблакіравана"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканіраванне твару"</string>
@@ -297,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запіс экрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Пачаць"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Спыніць"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Рэжым кіравання адной рукой"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблакіраваць мікрафон прылады?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблакіраваць камеру прылады?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблакіраваць камеру і мікрафон прылады?"</string>
@@ -472,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблакіраваць для выкарыстання"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Узнікла праблема з загрузкай вашых карт. Паўтарыце спробу пазней"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Налады экрана блакіроўкі"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Адсканіраваць QR-код"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Націсніце, каб адсканіраваць QR-код"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Працоўны профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Рэжым палёту"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Вы не пачуеце наступны будзільнік <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index 4ad97d2..72b9bc6 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Выключана"</item>
<item msgid="6866424167599381915">"Уключана"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Недаступна"</item>
+ <item msgid="3301403109049256043">"Выключана"</item>
+ <item msgid="8878684975184010135">"Уключана"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Недаступна"</item>
<item msgid="2710157085538036590">"Выключана"</item>
<item msgid="7809470840976856149">"Уключана"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Недаступна"</item>
+ <item msgid="146088982397753810">"Выключана"</item>
+ <item msgid="460891964396502657">"Уключана"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 951714e..ea68193 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласова помощ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Портфейл"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Инструмент за сканиране на QR кодове"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Отключване"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Устройството е заключено"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Извършва се сканиране на лице"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запис на екрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Старт"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Стоп"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се отблокира ли микрофонът на устройството?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се отблокира ли камерата на устройството?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се отблокират ли камерата и микрофонът на устройството?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отключване с цел използване"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"При извличането на картите ви възникна проблем. Моля, опитайте отново по-късно"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки за заключения екран"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканиране на QR код"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликнете, за да сканирате QR код"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Потребителски профил в Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Самолетен режим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Няма да чуете следващия си будилник в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index e7cc889..f0747cc 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Изкл."</item>
<item msgid="6866424167599381915">"Вкл."</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Не е налице"</item>
+ <item msgid="3301403109049256043">"Изключено"</item>
+ <item msgid="8878684975184010135">"Включено"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Не е налице"</item>
<item msgid="2710157085538036590">"Изкл."</item>
<item msgid="7809470840976856149">"Вкл."</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Не е налице"</item>
+ <item msgid="146088982397753810">"Изкл."</item>
+ <item msgid="460891964396502657">"Вкл."</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 217f9e3..377bd47 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ফোন"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ভয়েস সহায়তা"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ওয়ালেট"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR কোড স্ক্যানার"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"আনলক করুন"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ডিভাইস লক করা আছে"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ফেস স্ক্যান করা হচ্ছে"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"স্ক্রিন রেকর্ড"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"শুরু করুন"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"বন্ধ করুন"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এক হাতে ব্যবহার করার মোড"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইসের মাইক্রোফোন আনব্লক করতে চান?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইসের ক্যামেরা আনব্লক করতে চান?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইসের ক্যামেরা এবং মাইক্রোফোন আনব্লক করতে চান?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যবহার করতে আনলক করুন"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"আপনার কার্ড সংক্রান্ত তথ্য পেতে সমস্যা হয়েছে, পরে আবার চেষ্টা করুন"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্রিন সেটিংস"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR কোড স্ক্যান করুন"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR কোড স্ক্যান করতে ক্লিক করুন"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কাজের প্রোফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"বিমান মোড"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপনি আপনার পরবর্তী <xliff:g id="WHEN">%1$s</xliff:g> অ্যালার্ম শুনতে পাবেন না"</string>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index 584de5e..0530e46 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"বন্ধ আছে"</item>
<item msgid="6866424167599381915">"চালু আছে"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"উপলভ্য নয়"</item>
+ <item msgid="3301403109049256043">"বন্ধ করা আছে"</item>
+ <item msgid="8878684975184010135">"চালু আছে"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"উপলভ্য নেই"</item>
<item msgid="2710157085538036590">"বন্ধ আছে"</item>
<item msgid="7809470840976856149">"চালু আছে"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"উপলভ্য নয়"</item>
+ <item msgid="146088982397753810">"বন্ধ আছে"</item>
+ <item msgid="460891964396502657">"চালু আছে"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index efcb4d4..23eeb77 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Novčanik"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skener QR kôda"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Otključaj"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
@@ -295,6 +294,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Započnite"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokirati mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokirati kameru uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokirati kameru i mikrofon uređaja?"</string>
@@ -469,10 +469,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da koristite"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema prilikom preuzimanja vaših kartica. Pokušajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključavanja ekrana"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da skenirate QR kôd"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za posao"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u avionu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index 90b8cce..5d9edf5 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Isključeno"</item>
<item msgid="6866424167599381915">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nedostupno"</item>
+ <item msgid="3301403109049256043">"Isključeno"</item>
+ <item msgid="8878684975184010135">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nedostupno"</item>
<item msgid="2710157085538036590">"Isključeno"</item>
<item msgid="7809470840976856149">"Uključeno"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nedostupno"</item>
+ <item msgid="146088982397753810">"Isključeno"</item>
+ <item msgid="460891964396502657">"Uključeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8ddc456..446b0f4 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telèfon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistència per veu"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Cartera"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Escàner de codis QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloqueja"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositiu bloquejat"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"S\'està escanejant la cara"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravació de pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Inicia"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Atura"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vols desbloquejar el micròfon del dispositiu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vols desbloquejar la càmera del dispositiu?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vols desbloquejar la càmera i el micròfon del dispositiu?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloqueja per utilitzar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hi ha hagut un problema en obtenir les teves targetes; torna-ho a provar més tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuració de la pantalla de bloqueig"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escaneja un codi QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fes clic per escanejar un codi QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de treball"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode d\'avió"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> no sentiràs la pròxima alarma"</string>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 27bfb9c..55dfec0 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Desactivat"</item>
<item msgid="6866424167599381915">"Activat"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"No disponible"</item>
+ <item msgid="3301403109049256043">"Desactivat"</item>
+ <item msgid="8878684975184010135">"Activat"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"No disponible"</item>
<item msgid="2710157085538036590">"Desactivat"</item>
<item msgid="7809470840976856149">"Activat"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"No disponible"</item>
+ <item msgid="146088982397753810">"Desactivat"</item>
+ <item msgid="460891964396502657">"Activat"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 55c5333..37a827b 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hlasová asistence"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Peněženka"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Čtečka QR kódů"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Odemknout"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Zařízení uzamčeno"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skenování obličeje"</string>
@@ -297,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Záznam obrazovky"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Spustit"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončit"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokovat mikrofon zařízení?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokovat fotoaparát zařízení?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokovat fotoaparát a mikrofon zařízení?"</string>
@@ -472,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odemknout a použít"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Při načítání karet došlo k problému, zkuste to později"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavení obrazovky uzamčení"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Naskenovat QR kód"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknutím naskenujete QR kód"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovní profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim Letadlo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Svůj další budík <xliff:g id="WHEN">%1$s</xliff:g> neuslyšíte"</string>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index ee1f8fb..30f5bef 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Vyp"</item>
<item msgid="6866424167599381915">"Zap"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nedostupné"</item>
+ <item msgid="3301403109049256043">"Vypnuto"</item>
+ <item msgid="8878684975184010135">"Zapnuto"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nedostupné"</item>
<item msgid="2710157085538036590">"Vyp"</item>
<item msgid="7809470840976856149">"Zap"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nedostupné"</item>
+ <item msgid="146088982397753810">"Vypnuto"</item>
+ <item msgid="460891964396502657">"Zapnuto"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 2420c7f..bb4553f 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Taleassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kodescanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Lås op"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheden er låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanner ansigt"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skærmoptagelse"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du fjerne blokeringen af enhedens mikrofon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du fjerne blokeringen af enhedens kamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du fjerne blokeringen af enhedens kamera og mikrofon?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås op for at bruge"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Dine kort kunne ikke hentes. Prøv igen senere."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lås skærmindstillinger"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR-kode"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik for at scanne en QR-kode"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Du vil ikke kunne høre din næste alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index 770c2ed..0423949 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Fra"</item>
<item msgid="6866424167599381915">"Til"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Ikke tilgængelig"</item>
+ <item msgid="3301403109049256043">"Fra"</item>
+ <item msgid="8878684975184010135">"Til"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Ikke tilgængelig"</item>
<item msgid="2710157085538036590">"Fra"</item>
<item msgid="7809470840976856149">"Til"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Ikke tilgængelig"</item>
+ <item msgid="146088982397753810">"Fra"</item>
+ <item msgid="460891964396502657">"Til"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 61d489c..13ad7aa 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefonnummer"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Sprachassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-Code-Scanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Entsperren"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Gerät gesperrt"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Gesicht wird gescannt"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Bildschirmaufzeichnung"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Beenden"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Blockierung des Gerätemikrofons aufheben?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Blockierung der Gerätekamera aufheben?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blockierung von Gerätekamera und Gerätemikrofon aufheben?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Zum Verwenden entsperren"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Beim Abrufen deiner Karten ist ein Fehler aufgetreten – bitte versuch es später noch einmal"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Einstellungen für den Sperrbildschirm"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-Code scannen"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klicken, um einen QR-Code zu scannen"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbeitsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugmodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Lautloser Weckruf <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index 50d0ed5..a67ce6c 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Aus"</item>
<item msgid="6866424167599381915">"An"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nicht verfügbar"</item>
+ <item msgid="3301403109049256043">"Aus"</item>
+ <item msgid="8878684975184010135">"An"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nicht verfügbar"</item>
<item msgid="2710157085538036590">"Aus"</item>
<item msgid="7809470840976856149">"An"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nicht verfügbar"</item>
+ <item msgid="146088982397753810">"Aus"</item>
+ <item msgid="460891964396502657">"An"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0bd3940..38534f4 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Τηλέφωνο"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Φωνητική υποβοήθηση"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Πορτοφόλι"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Σάρωση κωδικών QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Ξεκλείδωμα"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Η συσκευή κλειδώθηκε"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Σάρωση προσώπου"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Εγγραφή οθόνης"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Έναρξη"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Διακοπή"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Κατάργηση αποκλεισμού μικροφώνου συσκευής;"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Κατάργηση αποκλεισμού κάμερας συσκευής;"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Κατάργηση αποκλεισμού κάμερας και μικροφώνου συσκευής;"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ξεκλείδωμα για χρήση"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Παρουσιάστηκε πρόβλημα με τη λήψη των καρτών σας. Δοκιμάστε ξανά αργότερα"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ρυθμίσεις κλειδώματος οθόνης"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Σάρωση κωδικού QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Κάντε κλικ για σάρωση κωδικού QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Προφίλ εργασίας"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Λειτουργία πτήσης"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Δεν θα ακούσετε το επόμενο ξυπνητήρι σας <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index 76f98f5..55d162d 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Ανενεργό"</item>
<item msgid="6866424167599381915">"Ενεργό"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Μη διαθέσιμη"</item>
+ <item msgid="3301403109049256043">"Ανενεργή"</item>
+ <item msgid="8878684975184010135">"Ενεργή"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Μη διαθέσιμο"</item>
<item msgid="2710157085538036590">"Ανενεργό"</item>
<item msgid="7809470840976856149">"Ενεργό"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Μη διαθέσιμη"</item>
+ <item msgid="146088982397753810">"Ανενεργή"</item>
+ <item msgid="460891964396502657">"Ενεργή"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 9807774..8debf42 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR code scanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanning face"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Screen record"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 2215f2d..fea1f10 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Off"</item>
<item msgid="6866424167599381915">"On"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Unavailable"</item>
+ <item msgid="3301403109049256043">"Off"</item>
+ <item msgid="8878684975184010135">"On"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Unavailable"</item>
<item msgid="2710157085538036590">"Off"</item>
<item msgid="7809470840976856149">"On"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Unavailable"</item>
+ <item msgid="146088982397753810">"Off"</item>
+ <item msgid="460891964396502657">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 246d580..da144d8 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR code scanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanning face"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Screen record"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
index 2215f2d..fea1f10 100644
--- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Off"</item>
<item msgid="6866424167599381915">"On"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Unavailable"</item>
+ <item msgid="3301403109049256043">"Off"</item>
+ <item msgid="8878684975184010135">"On"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Unavailable"</item>
<item msgid="2710157085538036590">"Off"</item>
<item msgid="7809470840976856149">"On"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Unavailable"</item>
+ <item msgid="146088982397753810">"Off"</item>
+ <item msgid="460891964396502657">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 9807774..8debf42 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR code scanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanning face"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Screen record"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 2215f2d..fea1f10 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Off"</item>
<item msgid="6866424167599381915">"On"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Unavailable"</item>
+ <item msgid="3301403109049256043">"Off"</item>
+ <item msgid="8878684975184010135">"On"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Unavailable"</item>
<item msgid="2710157085538036590">"Off"</item>
<item msgid="7809470840976856149">"On"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Unavailable"</item>
+ <item msgid="146088982397753810">"Off"</item>
+ <item msgid="460891964396502657">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 9807774..8debf42 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Phone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR code scanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanning face"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Screen record"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 2215f2d..fea1f10 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Off"</item>
<item msgid="6866424167599381915">"On"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Unavailable"</item>
+ <item msgid="3301403109049256043">"Off"</item>
+ <item msgid="8878684975184010135">"On"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Unavailable"</item>
<item msgid="2710157085538036590">"Off"</item>
<item msgid="7809470840976856149">"On"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Unavailable"</item>
+ <item msgid="146088982397753810">"Off"</item>
+ <item msgid="460891964396502657">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 95d8074..bb95d28 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -292,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Screen record"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
index 2432ea3..78f4137 100644
--- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
@@ -161,4 +161,9 @@
<item msgid="2710157085538036590">"Off"</item>
<item msgid="7809470840976856149">"On"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Unavailable"</item>
+ <item msgid="146088982397753810">"Off"</item>
+ <item msgid="460891964396502657">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index bcacd1b..fd18230 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Teléfono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Escáner de código QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Escaneando rostro"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Grabación de pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo de una mano"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Quieres desbloquear el micrófono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Quieres desbloquear la cámara del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Quieres desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocurrió un problema al obtener las tarjetas; vuelve a intentarlo más tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración de pantalla de bloqueo"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Haz clic para escanear un código QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index cc167d5..d70aa53 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Desactivado"</item>
<item msgid="6866424167599381915">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"No disponible"</item>
+ <item msgid="3301403109049256043">"No"</item>
+ <item msgid="8878684975184010135">"Sí"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"No disponible"</item>
<item msgid="2710157085538036590">"Desactivado"</item>
<item msgid="7809470840976856149">"Activado"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"No disponible"</item>
+ <item msgid="146088982397753810">"No"</item>
+ <item msgid="460891964396502657">"Sí"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 88235fd..a2eb152 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Teléfono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Cartera"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Escáner de códigos QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Escaneando cara"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Grabación de pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Desbloquear el micrófono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Desbloquear la cámara del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Se ha producido un problema al obtener tus tarjetas. Inténtalo de nuevo más tarde."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ajustes de pantalla de bloqueo"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Haz clic para escanear un código QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index 1d1cd71..9773954 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Desactivado"</item>
<item msgid="6866424167599381915">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"No disponible"</item>
+ <item msgid="3301403109049256043">"Desactivado"</item>
+ <item msgid="8878684975184010135">"Activado"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"No disponible"</item>
<item msgid="2710157085538036590">"Desactivado"</item>
<item msgid="7809470840976856149">"Activado"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"No disponible"</item>
+ <item msgid="146088982397753810">"Desactivado"</item>
+ <item msgid="460891964396502657">"Activado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index c1b463f..f1acdda 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Häälabi"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Rahakott"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-koodi skanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Luku avamine"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Seade on lukustatud"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Näo skannimine"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekraanisalvestus"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Alustage"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Peatage"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ühekäerežiim"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kas tühistada seadme mikrofoni blokeerimine?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kas tühistada seadme kaamera blokeerimine?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kas tühistada seadme kaamera ja mikrofoni blokeerimine?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avage kasutamiseks"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Teie kaartide hankimisel ilmnes probleem, proovige hiljem uuesti"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukustuskuva seaded"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-koodi skannimine"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klõpsake QR-koodi skannimiseks"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Tööprofiil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lennukirežiim"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Te ei kuule järgmist äratust kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 044954d..1f14a1c 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Väljas"</item>
<item msgid="6866424167599381915">"Sees"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Pole saadaval"</item>
+ <item msgid="3301403109049256043">"Väljas"</item>
+ <item msgid="8878684975184010135">"Sees"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Pole saadaval"</item>
<item msgid="2710157085538036590">"Väljas"</item>
<item msgid="7809470840976856149">"Sees"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Pole saadaval"</item>
+ <item msgid="146088982397753810">"Väljas"</item>
+ <item msgid="460891964396502657">"Sees"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index a9a325a..f220cc7 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefonoa"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ahots-laguntza"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Zorroa"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR kodeen eskanerra"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desblokeatu"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Gailua blokeatuta dago"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Aurpegia eskaneatzen"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Pantaila-grabaketa"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hasi"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Gelditu"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Gailuaren mikrofonoa desblokeatu nahi duzu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Gailuaren kamera desblokeatu nahi duzu?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Gailuaren kamera eta mikrofonoa desblokeatu nahi dituzu?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desblokeatu erabiltzeko"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Arazo bat izan da txartelak eskuratzean. Saiatu berriro geroago."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Pantaila blokeatuaren ezarpenak"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Eskaneatu QR kode bat"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kode bat eskaneatzeko, sakatu hau"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profila"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hegaldi modua"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ez duzu entzungo hurrengo alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index bb6c384..88e7069 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Desaktibatuta"</item>
<item msgid="6866424167599381915">"Aktibatuta"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Ez dago erabilgarri"</item>
+ <item msgid="3301403109049256043">"Desaktibatuta"</item>
+ <item msgid="8878684975184010135">"Aktibatuta"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Ez dago erabilgarri"</item>
<item msgid="2710157085538036590">"Desaktibatuta"</item>
<item msgid="7809470840976856149">"Aktibatuta"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Ez dago erabilgarri"</item>
+ <item msgid="146088982397753810">"Desaktibatuta"</item>
+ <item msgid="460891964396502657">"Aktibatuta"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 9260abd..08d13fd 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"تلفن"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"دستیار صوتی"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"اسکنر رمزینه پاسخسریع"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"باز کردن قفل"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"دستگاه قفل است"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"درحال اسکن کردن چهره"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ضبط صفحهنمایش"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"شروع"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"توقف"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"حالت تک حرکت"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"دوربین و میکروفون دستگاه لغو انسداد شود؟"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"برای استفاده، قفل را باز کنید"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"هنگام دریافت کارتها مشکلی پیش آمد، لطفاً بعداً دوباره امتحان کنید"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"تنظیمات صفحه قفل"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"اسکن رمزینه پاسخسریع"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"برای اسکن رمزینه پاسخسریع، کلیک کنید"</string>
<string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"در ساعت <xliff:g id="WHEN">%1$s</xliff:g>، دیگر صدای زنگ ساعت را نمیشنوید"</string>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index 13c7f41..c3d6169 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"خاموش"</item>
<item msgid="6866424167599381915">"روشن"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"دردسترس نیست"</item>
+ <item msgid="3301403109049256043">"خاموش"</item>
+ <item msgid="8878684975184010135">"روشن"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"دردسترس نیست"</item>
<item msgid="2710157085538036590">"خاموش"</item>
<item msgid="7809470840976856149">"روشن"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"دردسترس نیست"</item>
+ <item msgid="146088982397753810">"خاموش"</item>
+ <item msgid="460891964396502657">"روشن"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 5c36c7c..f0410e8 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Puhelin"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ääniapuri"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-koodiskanneri"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Avaa lukitus"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Laite lukittu"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Kasvojen skannaus"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Näytön tallennus"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Aloita"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Lopeta"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kumotaanko laitteen mikrofonin esto?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kumotaanko laitteen kameran esto?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kumotaanko laitteen kameran ja mikrofonin esto?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avaa lukitus ja käytä"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Korttien noutamisessa oli ongelma, yritä myöhemmin uudelleen"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukitusnäytön asetukset"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skannaa QR-koodi"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Skannaa QR-koodi klikkaamalla"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Työprofiili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lentokonetila"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Et kuule seuraavaa hälytystäsi (<xliff:g id="WHEN">%1$s</xliff:g>)."</string>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index 47013d1..7e7468d 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Poissa päältä"</item>
<item msgid="6866424167599381915">"Päällä"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Ei saatavilla"</item>
+ <item msgid="3301403109049256043">"Poissa päältä"</item>
+ <item msgid="8878684975184010135">"Päällä"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Ei saatavilla"</item>
<item msgid="2710157085538036590">"Poissa päältä"</item>
<item msgid="7809470840976856149">"Päällä"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Ei saatavilla"</item>
+ <item msgid="146088982397753810">"Poissa päältä"</item>
+ <item msgid="460891964396502657">"Päällä"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c5d8a48..1825a02 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Téléphone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistance vocale"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portefeuille"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Lecteur de code QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Déverrouiller"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Numérisation du visage"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement de l\'écran"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le microphone de l\'appareil?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer l\'appareil photo de l\'appareil?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le microphone?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Un problème est survenu lors de la récupération de vos cartes, veuillez réessayer plus tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Numériser le code QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Cliquez pour numériser un code QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index fb929fc..bce1dde 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Désactivé"</item>
<item msgid="6866424167599381915">"Activé"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Non accessible"</item>
+ <item msgid="3301403109049256043">"Désactivé"</item>
+ <item msgid="8878684975184010135">"Activé"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Non disponible"</item>
<item msgid="2710157085538036590">"Désactivée"</item>
<item msgid="7809470840976856149">"Activée"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Indisponible"</item>
+ <item msgid="146088982397753810">"Désactivé"</item>
+ <item msgid="460891964396502657">"Activé"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index ead26a3..8b6a490 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Téléphoner"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistance vocale"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portefeuille"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Lecteur de code QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Déverrouiller"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Analyse du visage en cours"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement de l\'écran"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le micro de l\'appareil ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer l\'appareil photo de l\'appareil ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le micro de l\'appareil ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Problème de récupération de vos cartes. Réessayez plus tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scanner un code QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Cliquer pour scanner un code QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme <xliff:g id="WHEN">%1$s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 971477d..a2e2f73 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Désactivé"</item>
<item msgid="6866424167599381915">"Activé"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Indisponible"</item>
+ <item msgid="3301403109049256043">"Désactivé"</item>
+ <item msgid="8878684975184010135">"Activé"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Indisponible"</item>
<item msgid="2710157085538036590">"Désactivée"</item>
<item msgid="7809470840976856149">"Activée"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Indisponible"</item>
+ <item msgid="146088982397753810">"Désactivé"</item>
+ <item msgid="460891964396502657">"Activé"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 3aa3e3f..6c21265 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Teléfono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente de voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Escáner de código QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Analizando cara"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravar pant."</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Deter"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Queres desbloquear o micrófono do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Queres desbloquear a cámara do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Queres desbloquear a cámara e o micrófono do dispositivo?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Produciuse un problema ao obter as tarxetas. Téntao de novo máis tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración da pantalla de bloqueo"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fai clic para escanear un código QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de traballo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Non escoitarás a alarma seguinte <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index e362238..7819fbb 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Non"</item>
<item msgid="6866424167599381915">"Si"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Non dispoñible"</item>
+ <item msgid="3301403109049256043">"Desactivado"</item>
+ <item msgid="8878684975184010135">"Activado"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Non dispoñible"</item>
<item msgid="2710157085538036590">"Non"</item>
<item msgid="7809470840976856149">"Si"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Non dispoñible"</item>
+ <item msgid="146088982397753810">"Desactivado"</item>
+ <item msgid="460891964396502657">"Activado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index be707d8..f1bd364 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ફોન"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"વૉઇસ સહાય"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"વૉલેટ"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR કોડ સ્કૅનર"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"અનલૉક કરો"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ડિવાઇસ લૉક કરેલું છે"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ચહેરો સ્કૅન કરવો"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"સ્ક્રીન રેકૉર્ડ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"શરૂ કરો"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"રોકો"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"એક-હાથે વાપરો મોડ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ડિવાઇસના માઇક્રોફોનને કરીએ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ડિવાઇસના કૅમેરાને અનબ્લૉક કરીએ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ડિવાઇસના કૅમેરા અને માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ઉપયોગ કરવા માટે અનલૉક કરો"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"તમારા કાર્ડની માહિતી મેળવવામાં સમસ્યા આવી હતી, કૃપા કરીને થોડા સમય પછી ફરી પ્રયાસ કરો"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"લૉક સ્ક્રીનના સેટિંગ"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR સ્કૅન કરો"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR કોડ સ્કૅન કરવા માટે ક્લિક કરો"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ઑફિસની પ્રોફાઇલ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"એરપ્લેન મોડ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"તમે <xliff:g id="WHEN">%1$s</xliff:g> એ તમારો આગલો એલાર્મ સાંભળશો નહીં"</string>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index 12c2de7..ddf18f6 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"બંધ છે"</item>
<item msgid="6866424167599381915">"ચાલુ છે"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"અનુપલબ્ધ"</item>
+ <item msgid="3301403109049256043">"બંધ છે"</item>
+ <item msgid="8878684975184010135">"ચાલુ છે"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"ઉપલબ્ધ નથી"</item>
<item msgid="2710157085538036590">"બંધ છે"</item>
<item msgid="7809470840976856149">"ચાલુ છે"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"અનુપલબ્ધ"</item>
+ <item msgid="146088982397753810">"બંધ છે"</item>
+ <item msgid="460891964396502657">"ચાલુ છે"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 80b52d2..72eb309 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"फ़ोन"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"आवाज़ से डिवाइस का इस्तेमाल"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"वॉलेट बटन"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"क्यूआर कोड स्कैनर"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"अनलॉक करें"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"डिवाइस लॉक है"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"डिवाइस अनलॉक करने के लिए चेहरा स्कैन किया जाता है"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"स्क्रीन रिकॉर्डर"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"शुरू करें"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"रोकें"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आप डिवाइस के माइक्रोफ़ोन को अनब्लॉक करना चाहते हैं?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आप डिवाइस के कैमरे को अनब्लॉक करना चाहते हैं?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"क्या आप डिवाइस का कैमरा और माइक्रोफ़ोन अनब्लॉक करना चाहते हैं?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"इस्तेमाल करने के लिए, डिवाइस अनलॉक करें"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"आपके कार्ड की जानकारी पाने में कोई समस्या हुई है. कृपया बाद में कोशिश करें"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन की सेटिंग"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"क्यूआर कोड स्कैन करें"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"क्यूआर कोड स्कैन करने के लिए क्लिक करें"</string>
<string name="status_bar_work" msgid="5238641949837091056">"वर्क प्रोफ़ाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाई जहाज़ मोड"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"आपको <xliff:g id="WHEN">%1$s</xliff:g> पर अपना अगला अलार्म नहीं सुनाई देगा"</string>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index f7fce26..08db65b 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"बंद है"</item>
<item msgid="6866424167599381915">"चालू है"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"उपलब्ध नहीं है"</item>
+ <item msgid="3301403109049256043">"बंद है"</item>
+ <item msgid="8878684975184010135">"चालू है"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"उपलब्ध नहीं है"</item>
<item msgid="2710157085538036590">"बंद है"</item>
<item msgid="7809470840976856149">"चालू है"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"उपलब्ध नहीं है"</item>
+ <item msgid="146088982397753810">"बंद है"</item>
+ <item msgid="460891964396502657">"चालू है"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 8864901..e549d725 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Čitač QR koda"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Otključavanje"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
@@ -295,6 +294,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje zaslona"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Početak"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavi"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite li deblokirati mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite li deblokirati fotoaparat uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite li deblokirati fotoaparat i mikrofon uređaja?"</string>
@@ -469,10 +469,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da biste koristili"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pojavio se problem prilikom dohvaćanja kartica, pokušajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključanog zaslona"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR kôd"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da biste skenirali QR kôd"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u zrakoplovu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index 90b8cce..5d9edf5 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Isključeno"</item>
<item msgid="6866424167599381915">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nedostupno"</item>
+ <item msgid="3301403109049256043">"Isključeno"</item>
+ <item msgid="8878684975184010135">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nedostupno"</item>
<item msgid="2710157085538036590">"Isključeno"</item>
<item msgid="7809470840976856149">"Uključeno"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nedostupno"</item>
+ <item msgid="146088982397753810">"Isključeno"</item>
+ <item msgid="460891964396502657">"Uključeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index d8f2238..5bb6dd0 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hangsegéd"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kód-szkennelő"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Feloldás"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Az eszköz zárolva van"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Arc keresése"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Képernyőrögzítés"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Indítás"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Leállítás"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Feloldja az eszközmikrofon letiltását?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Feloldja az eszközkamera letiltását?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Feloldja az eszközkamera és -mikrofon letiltását?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Oldja fel a használathoz"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Probléma merült fel a kártyák lekérésekor, próbálja újra később"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lezárási képernyő beállításai"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-kód beolvasása"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kattintson a QR-kód beolvasához"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Munkahelyi profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Repülős üzemmód"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nem fogja hallani az ébresztést ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 6e1d636..dbfdf99 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Ki"</item>
<item msgid="6866424167599381915">"Be"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nem áll rendelkezésre"</item>
+ <item msgid="3301403109049256043">"Ki"</item>
+ <item msgid="8878684975184010135">"Be"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nem áll rendelkezésre"</item>
<item msgid="2710157085538036590">"Ki"</item>
<item msgid="7809470840976856149">"Be"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nem áll rendelkezésre"</item>
+ <item msgid="146088982397753810">"Ki"</item>
+ <item msgid="460891964396502657">"Be"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index c5f5595..4b02f8b 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Հեռախոս"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ձայնային հուշումներ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Դրամապանակ"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR կոդերի սկաներ"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Ապակողպել"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Սարքը կողպված է"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Դեմքի սկանավորում"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Էկրանի տեսագրում"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Սկսել"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Կանգնեցնել"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Մեկ ձեռքի ռեժիմ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Արգելահանե՞լ սարքի խոսափողը"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Արգելահանե՞լ սարքի տեսախցիկը"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Արգելահանե՞լ սարքի տեսախցիկը և խոսափողը"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ապակողպել՝ օգտագործելու համար"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Չհաջողվեց բեռնել քարտերը։ Նորից փորձեք։"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Կողպէկրանի կարգավորումներ"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR կոդերի սկանավորում"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Սեղմեք՝ QR կոդը սկանավորելու համար"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Android for Work-ի պրոֆիլ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ավիառեժիմ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ժամը <xliff:g id="WHEN">%1$s</xliff:g>-ի զարթուցիչը չի զանգի"</string>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index 3e9c28c..323d292 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Անջատված է"</item>
<item msgid="6866424167599381915">"Միացված է"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Անհասանելի է"</item>
+ <item msgid="3301403109049256043">"Անջատված է"</item>
+ <item msgid="8878684975184010135">"Միացված է"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Հասանելի չէ"</item>
<item msgid="2710157085538036590">"Անջատված է"</item>
<item msgid="7809470840976856149">"Միացված է"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Հասանելի չէ"</item>
+ <item msgid="146088982397753810">"Անջատված է"</item>
+ <item msgid="460891964396502657">"Միացված է"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 1bae18e..546f694 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Edit"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Mengedit screenshot"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Bagikan screenshot"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Ambil lebih banyak"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Menutup screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string>
@@ -108,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telepon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Bantuan Suara"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Pemindai Kode QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Buka kunci"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Perangkat terkunci"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Memindai wajah"</string>
@@ -294,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Perekam layar"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mulai"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Berhenti memblokir mikrofon perangkat?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Berhenti memblokir kamera perangkat?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Berhenti memblokir kamera dan mikrofon perangkat?"</string>
@@ -467,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Terjadi masalah saat mendapatkan kartu Anda, coba lagi nanti"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setelan layar kunci"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Pindai QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik untuk memindai kode QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar alarm berikutnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index 70ee1bc..29d50b5 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Nonaktif"</item>
<item msgid="6866424167599381915">"Aktif"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Tidak tersedia"</item>
+ <item msgid="3301403109049256043">"Nonaktif"</item>
+ <item msgid="8878684975184010135">"Aktif"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Tidak tersedia"</item>
<item msgid="2710157085538036590">"Nonaktif"</item>
<item msgid="7809470840976856149">"Aktif"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Tidak tersedia"</item>
+ <item msgid="146088982397753810">"Nonaktif"</item>
+ <item msgid="460891964396502657">"Aktif"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 9c3889d..31fe363 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Sími"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Raddaðstoð"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Veski"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kóðaskanni"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Taka úr lás"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Tækið er læst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Andlit skannað"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skjáupptaka"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hefja"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stöðva"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Opna fyrir hljóðnema tækisins?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Opna fyrir myndavél tækisins?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Opna fyrir myndavél og hljóðnema tækisins?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Taktu úr lás til að nota"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Vandamál kom upp við að sækja kortin þín. Reyndu aftur síðar"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Stillingar fyrir læstan skjá"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanna QR-kóða"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Smelltu til að skanna QR-kóða"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Vinnusnið"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugstilling"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ekki mun heyrast í vekjaranum <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index 3565a9c..4d9a097 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Slökkt"</item>
<item msgid="6866424167599381915">"Kveikt"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Ekki í boði"</item>
+ <item msgid="3301403109049256043">"Slökkt"</item>
+ <item msgid="8878684975184010135">"Kveikt"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Ekki í boði"</item>
<item msgid="2710157085538036590">"Slökkt"</item>
<item msgid="7809470840976856149">"Kveikt"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Ekki í boði"</item>
+ <item msgid="146088982397753810">"Slökkt"</item>
+ <item msgid="460891964396502657">"Kveikt"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 4a5530f..3b54801 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portafoglio"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Scanner codici QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Sblocca"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloccato"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scansione del viso"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Registrazione dello schermo"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Inizia"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Interrompi"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità one-hand"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vuoi sbloccare il microfono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vuoi sbloccare la fotocamera del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vuoi sbloccare la fotocamera e il microfono del dispositivo?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Sblocca per usare"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Si è verificato un problema durante il recupero delle tue carte. Riprova più tardi."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Impostazioni schermata di blocco"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scansiona QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fai clic per scansionare un codice QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profilo di lavoro"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modalità aereo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Non sentirai la tua prossima sveglia <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index a9c67d5..db0bbb4 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Off"</item>
<item msgid="6866424167599381915">"On"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Non disponibile"</item>
+ <item msgid="3301403109049256043">"Off"</item>
+ <item msgid="8878684975184010135">"On"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Non disponibile"</item>
<item msgid="2710157085538036590">"Off"</item>
<item msgid="7809470840976856149">"On"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Non disponibile"</item>
+ <item msgid="146088982397753810">"Off"</item>
+ <item msgid="460891964396502657">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 9368808..066f3c2 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"טלפון"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"האסיסטנט"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ארנק"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"סורק קודי QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ביטול נעילה"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"המכשיר נעול"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"סורק פנים"</string>
@@ -297,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"הקלטת המסך"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"התחלה"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"עצירה"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"מצב שימוש ביד אחת"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"לבטל את חסימת המיקרופון של המכשיר?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"לבטל את חסימת המצלמה של המכשיר?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"לבטל את חסימת המצלמה והמיקרופון של המכשיר?"</string>
@@ -472,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"יש לבטל את הנעילה כדי להשתמש"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"הייתה בעיה בקבלת הכרטיסים שלך. כדאי לנסות שוב מאוחר יותר"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"הגדרות מסך הנעילה"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"סריקת קוד QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"צריך ללחוץ כאן כדי לסרוק קוד QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"פרופיל עבודה"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"מצב טיסה"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"לא ניתן יהיה לשמוע את ההתראה הבאה שלך <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index c769a84..61735cf 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"כבוי"</item>
<item msgid="6866424167599381915">"פועל"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"לא זמין"</item>
+ <item msgid="3301403109049256043">"כבוי"</item>
+ <item msgid="8878684975184010135">"פועל"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"לא זמין"</item>
<item msgid="2710157085538036590">"כבוי"</item>
<item msgid="7809470840976856149">"פועל"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"לא זמין"</item>
+ <item msgid="146088982397753810">"כבוי"</item>
+ <item msgid="460891964396502657">"פועל"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index bb35ad8..4295e96 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"電話"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"音声アシスト"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ウォレット"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR コードスキャナ"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ロック解除"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"デバイスはロックされています"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"顔のスキャン"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"スクリーン レコード"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"片手モード"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"デバイスのマイクのブロックを解除しますか?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"デバイスのカメラのブロックを解除しますか?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"デバイスのカメラとマイクのブロックを解除しますか?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ロックを解除して使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"カードの取得中に問題が発生しました。しばらくしてからもう一度お試しください"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ロック画面の設定"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR のスキャン"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"クリックすると、QR コードをスキャンします"</string>
<string name="status_bar_work" msgid="5238641949837091056">"仕事用プロファイル"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"機内モード"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"次回のアラーム(<xliff:g id="WHEN">%1$s</xliff:g>)は鳴りません"</string>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index 6383acc..b65d2f8 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"OFF"</item>
<item msgid="6866424167599381915">"ON"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"使用不可"</item>
+ <item msgid="3301403109049256043">"OFF"</item>
+ <item msgid="8878684975184010135">"ON"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"使用不可"</item>
<item msgid="2710157085538036590">"OFF"</item>
<item msgid="7809470840976856149">"ON"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"使用不可"</item>
+ <item msgid="146088982397753810">"OFF"</item>
+ <item msgid="460891964396502657">"ON"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 94eb7c2..9ab002a 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ტელეფონი"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ხმოვანი დახმარება"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR კოდის სკანერი"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"განბლოკვა"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"მოწყობილობა ჩაკეტილია"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"მიმდინარეობს სახის სკანირება"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ეკრანის ჩანაწერი"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"დაწყება"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"შეწყვეტა"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ცალი ხელის რეჟიმი"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"გამოსაყენებლად განბლოკვა"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"თქვენი ბარათების მიღებისას პრობლემა წარმოიშვა. ცადეთ ხელახლა მოგვიანებით"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ჩაკეტილი ეკრანის პარამეტრები"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-ის სკანირება"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"დააწკაპუნეთ QR კოდის სკანირებისთვის"</string>
<string name="status_bar_work" msgid="5238641949837091056">"სამსახურის პროფილი"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"თვითმფრინავის რეჟიმი"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ვერ გაიგონებთ მომდევნო მაღვიძარას <xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index 4c23237..4e621a0 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"გამორთულია"</item>
<item msgid="6866424167599381915">"ჩართულია"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"მიუწვდომელია"</item>
+ <item msgid="3301403109049256043">"გამორთვა"</item>
+ <item msgid="8878684975184010135">"ჩართვა"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"მიუწვდომელია"</item>
<item msgid="2710157085538036590">"გამორთულია"</item>
<item msgid="7809470840976856149">"ჩართულია"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"მიუწვდომელია"</item>
+ <item msgid="146088982397753810">"გამორთვა"</item>
+ <item msgid="460891964396502657">"ჩართვა"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 18ce927..623d329 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Дауыс көмекшісі"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Әмиян"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR кодын сканерлеу қолданбасы"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Бекітпесін ашу"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Құрылғы құлыпталды."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Бетті сканерлеу"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Экранды жазу"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Бастау"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Тоқтату"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Құрылғы микрофонының бөгеуі алынсын ба?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Құрылғы камерасының бөгеуі алынсын ба?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Құрылғы камерасы мен микрофонының бөгеуі алынсын ба?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Пайдалану үшін құлыпты ашу"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Карталарыңыз алынбады, кейінірек қайталап көріңіз."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Экран құлпының параметрлері"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR кодын сканерлеу"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодын сканерлеу үшін басыңыз."</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Келесі <xliff:g id="WHEN">%1$s</xliff:g> дабылыңызды есітпейсіз"</string>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index 7a4676f..d3ad572 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Өшірулі"</item>
<item msgid="6866424167599381915">"Қосулы"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Қолжетімді емес"</item>
+ <item msgid="3301403109049256043">"Өшірулі"</item>
+ <item msgid="8878684975184010135">"Қосулы"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Қолжетімсіз"</item>
<item msgid="2710157085538036590">"Өшірулі"</item>
<item msgid="7809470840976856149">"Қосулы"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Қолжетімді емес"</item>
+ <item msgid="146088982397753810">"Өшірулі"</item>
+ <item msgid="460891964396502657">"Қосулы"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index b86f9b5..75f5a0a 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ទូរសព្ទ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ជំនួយសំឡេង"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"កាបូប"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"កម្មវិធីស្កេនកូដ QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ដោះសោ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"បានចាក់សោឧបករណ៍"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ការស្កេនមុខ"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ការថតវីដេអូអេក្រង់"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ចាប់ផ្ដើម"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ឈប់"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រើដៃម្ខាង"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ឈប់ទប់ស្កាត់មីក្រូហ្វូនរបស់ឧបករណ៍ឬ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ឈប់ទប់ស្កាត់កាមេរ៉ារបស់ឧបករណ៍ឬ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ឈប់ទប់ស្កាត់កាមេរ៉ា និងមីក្រូហ្វូនរបស់ឧបករណ៍ឬ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ដោះសោដើម្បីប្រើប្រាស់"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"មានបញ្ហាក្នុងការទាញយកកាតរបស់អ្នក សូមព្យាយាមម្ដងទៀតនៅពេលក្រោយ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ការកំណត់អេក្រង់ចាក់សោ"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"ស្កេនកូដ QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"ចុចដើម្បីស្កេនកូដ QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ប្រវត្តិរូបការងារ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ពេលជិះយន្តហោះ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"អ្នកនឹងមិនលឺម៉ោងរោទ៍ <xliff:g id="WHEN">%1$s</xliff:g> បន្ទាប់របស់អ្នកទេ"</string>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index be3f754..f67aafb 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"បិទ"</item>
<item msgid="6866424167599381915">"បើក"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"មិនអាចប្រើបានទេ"</item>
+ <item msgid="3301403109049256043">"បិទ"</item>
+ <item msgid="8878684975184010135">"បើក"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"មិនមានទេ"</item>
<item msgid="2710157085538036590">"បិទ"</item>
<item msgid="7809470840976856149">"បើក"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"មិនមានទេ"</item>
+ <item msgid="146088982397753810">"បិទ"</item>
+ <item msgid="460891964396502657">"បើក"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 614b0ca..ce594f7 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ಫೋನ್"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನರ್"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ಅನ್ಲಾಕ್"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ಸಾಧನ ಲಾಕ್ ಆಗಿದೆ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ಮುಖವನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ನಿಲ್ಲಿಸಿ"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ಒಂದು ಕೈ ಮೋಡ್"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ಸಾಧನದ ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಅನ್ಬ್ಲಾಕ್ ಮಾಡಬೇಕೇ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ಬಳಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ನಿಮ್ಮ ಕಾರ್ಡ್ಗಳನ್ನು ಪಡೆಯುವಾಗ ಸಮಸ್ಯೆ ಉಂಟಾಗಿದೆ, ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ಲಾಕ್ ಸ್ಕ್ರ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್ಪ್ಲೇನ್ ಮೋಡ್"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ನಿಮ್ಮ ಮುಂದಿನ <xliff:g id="WHEN">%1$s</xliff:g> ಅಲಾರಮ್ ಅನ್ನು ನೀವು ಆಲಿಸುವುದಿಲ್ಲ"</string>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 7eea89d..26ec958 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ಆಫ್ ಮಾಡಿ"</item>
<item msgid="6866424167599381915">"ಆನ್ ಮಾಡಿ"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"ಲಭ್ಯವಿಲ್ಲ"</item>
+ <item msgid="3301403109049256043">"ಆಫ್ ಮಾಡಿ"</item>
+ <item msgid="8878684975184010135">"ಆನ್ ಮಾಡಿ"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"ಲಭ್ಯವಿಲ್ಲ"</item>
<item msgid="2710157085538036590">"ಆಫ್ ಮಾಡಿ"</item>
<item msgid="7809470840976856149">"ಆನ್ ಮಾಡಿ"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"ಲಭ್ಯವಿಲ್ಲ"</item>
+ <item msgid="146088982397753810">"ಆಫ್ ಮಾಡಿ"</item>
+ <item msgid="460891964396502657">"ಆನ್ ಮಾಡಿ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index fff2b55..e742659 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"전화"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"음성 지원"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"지갑"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR 코드 스캐너"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"잠금 해제"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"기기 잠김"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"얼굴 스캔 중"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"화면 녹화"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"시작"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"중지"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 사용 모드"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"기기 마이크를 차단 해제하시겠습니까?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"기기 카메라를 차단 해제하시겠습니까?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"기기 카메라 및 마이크를 차단 해제하시겠습니까?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"잠금 해제하여 사용"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"카드를 가져오는 중에 문제가 발생했습니다. 나중에 다시 시도해 보세요."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"잠금 화면 설정"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR 스캔"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR 코드를 스캔하려면 클릭하세요."</string>
<string name="status_bar_work" msgid="5238641949837091056">"직장 프로필"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"비행기 모드"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>에 다음 알람을 들을 수 없습니다."</string>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index fd03b4d..0c22971 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"꺼짐"</item>
<item msgid="6866424167599381915">"켜짐"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"사용 불가"</item>
+ <item msgid="3301403109049256043">"꺼짐"</item>
+ <item msgid="8878684975184010135">"켜짐"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"이용 불가"</item>
<item msgid="2710157085538036590">"꺼짐"</item>
<item msgid="7809470840976856149">"켜짐"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"사용 불가"</item>
+ <item msgid="146088982397753810">"꺼짐"</item>
+ <item msgid="460891964396502657">"켜짐"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 81e3d5d..f6fe747 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Үн жардамчысы"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Капчык"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR кодунун сканери"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Кулпусун ачуу"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Түзмөк кулпуланды"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Жүз скандалууда"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Экрандан видео жаздырып алуу"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Баштадык"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Токтотуу"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Түзмөктүн микрофонун бөгөттөн чыгарасызбы?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Түзмөктүн камерасын бөгөттөн чыгарасызбы?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Түзмөктүн камерасы менен микрофону бөгөттөн чыгарылсынбы?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Колдонуу үчүн кулпусун ачыңыз"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Кыйытмаларды алууда ката кетти. Бир аздан кийин кайталап көрүңүз."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Кулпуланган экран жөндөөлөрү"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR кодун скандоо"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодун скандоо үчүн чыкылдатыңыз"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жумуш профили"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Учак режими"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> боло турган кийинки эскертмени укпайсыз"</string>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 27aabb8..ae6520e 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Өчүк"</item>
<item msgid="6866424167599381915">"Күйүк"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Жеткиликсиз"</item>
+ <item msgid="3301403109049256043">"Өчүк"</item>
+ <item msgid="8878684975184010135">"Күйүк"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Жеткиликсиз"</item>
<item msgid="2710157085538036590">"Өчүк"</item>
<item msgid="7809470840976856149">"Күйүк"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Жеткиликсиз"</item>
+ <item msgid="146088982397753810">"Өчүк"</item>
+ <item msgid="460891964396502657">"Күйүк"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index ed5db04..c401823 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ໂທລະສັບ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ຊ່ວຍເຫຼືອທາງສຽງ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ກະເປົາ"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"ຕົວສະແກນລະຫັດ QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ປົດລັອກ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ອຸປະກອນຖືກລັອກໄວ້"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ການສະແກນໜ້າ"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ບັນທຶກໜ້າຈໍ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ເລີ່ມ"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ຢຸດ"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝດມືດຽວ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ຍົກເລີກການບລັອກໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ຍົກເລີກການບລັອກກ້ອງຖ່າຍຮູບອຸປະກອນບໍ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ຍົກເລີກການບລັອກກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ປົດລັອກເພື່ອໃຊ້"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ເກີດບັນຫາໃນການໂຫຼດບັດຂອງທ່ານ, ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ການຕັ້ງຄ່າໜ້າຈໍລັອກ"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"ສະແກນ QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"ຄລິກເພື່ອສະແກນລະຫັດ QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ໂໝດເຮືອບິນ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ທ່ານຈະບໍ່ໄດ້ຍິນສຽງໂມງປ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index cbb4e9d..e818a09 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ປິດ"</item>
<item msgid="6866424167599381915">"ເປີດ"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
+ <item msgid="3301403109049256043">"ປິດ"</item>
+ <item msgid="8878684975184010135">"ເປີດ"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
<item msgid="2710157085538036590">"ປິດ"</item>
<item msgid="7809470840976856149">"ເປີດ"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
+ <item msgid="146088982397753810">"ປິດ"</item>
+ <item msgid="460891964396502657">"ເປີດ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index b040dde..2670ce4 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefonas"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Piniginė"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR kodų skaitytuvas"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Atrakinti"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Įrenginys užrakintas"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Nuskaitomas veidas"</string>
@@ -297,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekrano įrašas"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Pradėti"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stabdyti"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos režimas"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Panaikinti įrenginio mikrofono blokavimą?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Panaikinti įrenginio fotoaparato blokavimą?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Panaikinti įrenginio fotoaparato ir mikrofono blokavimą?"</string>
@@ -472,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Atrakinti, kad būtų galima naudoti"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Gaunant korteles kilo problema, bandykite dar kartą vėliau"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Užrakinimo ekrano nustatymai"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Nuskaityti QR kodą"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Spustelėkite, kad nuskaitytumėte QR kodą"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darbo profilis"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lėktuvo režimas"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Negirdėsite kito signalo <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index c881b1e..28d4a73 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Išjungta"</item>
<item msgid="6866424167599381915">"Įjungta"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nepasiekiama"</item>
+ <item msgid="3301403109049256043">"Išjungta"</item>
+ <item msgid="8878684975184010135">"Įjungta"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nepasiekiama"</item>
<item msgid="2710157085538036590">"Išjungta"</item>
<item msgid="7809470840976856149">"Įjungta"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nepasiekiama"</item>
+ <item msgid="146088982397753810">"Išjungta"</item>
+ <item msgid="460891964396502657">"Įjungta"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 031f09e..16e2eef 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Tālruņa numurs"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Balss palīgs"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Maks"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Ātrās atbildes koda skeneris"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Atbloķēt"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Ierīce ir bloķēta"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Sejas skenēšana"</string>
@@ -295,6 +294,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekrāna ierakstīšana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Sākt"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Apturēt"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas režīms"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vai atbloķēt ierīces mikrofonu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vai vēlaties atbloķēt ierīces kameru?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vai atbloķēt ierīces kameru un mikrofonu?"</string>
@@ -469,10 +469,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lai izmantotu, atbloķējiet ekrānu"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ienesot jūsu kartes, radās problēma. Lūdzu, vēlāk mēģiniet vēlreiz."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Bloķēšanas ekrāna iestatījumi"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ātrās atbildes koda skeneris"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Noklikšķiniet, lai skenētu ātrās atbildes kodu."</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darba profils"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lidojuma režīms"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nākamais signāls (<xliff:g id="WHEN">%1$s</xliff:g>) netiks atskaņots."</string>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index 2f170e0..ed0baf2 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Izslēgts"</item>
<item msgid="6866424167599381915">"Ieslēgts"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nav pieejams"</item>
+ <item msgid="3301403109049256043">"Izslēgts"</item>
+ <item msgid="8878684975184010135">"Ieslēgts"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nav pieejams"</item>
<item msgid="2710157085538036590">"Izslēgts"</item>
<item msgid="7809470840976856149">"Ieslēgts"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nav pieejams"</item>
+ <item msgid="146088982397753810">"Izslēgts"</item>
+ <item msgid="460891964396502657">"Ieslēgts"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index ad7818a..de05345 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласовна помош"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Паричник"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Скенер на QR-кодови"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Отклучување"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Уредот е заклучен"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скенирање лице"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Снимање екран"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Започни"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Сопри"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим со една рака"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се одблокира пристапот до микрофонот на уредот?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се одблокира пристапот до камерата на уредот?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се одблокира пристапот до камерата и микрофонот на уредот?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отклучете за да користите"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Имаше проблем при преземањето на картичките. Обидете се повторно подоцна"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Поставки за заклучен екран"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Скенирајте QR-код"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликнете за да скенирате QR-код"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Работен профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Авионски режим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Нема да го слушнете следниот аларм <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 912746a..5c36715 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Исклучено"</item>
<item msgid="6866424167599381915">"Вклучено"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Недостапен"</item>
+ <item msgid="3301403109049256043">"Исклучен"</item>
+ <item msgid="8878684975184010135">"Вклучен"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Недостапно"</item>
<item msgid="2710157085538036590">"Исклучено"</item>
<item msgid="7809470840976856149">"Вклучено"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Недостапен"</item>
+ <item msgid="146088982397753810">"Исклучен"</item>
+ <item msgid="460891964396502657">"Вклучен"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 552d228..e7a5c33 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ഫോണ്"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"വോയ്സ് സഹായം"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"വാലറ്റ്"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR കോഡ് സ്കാനർ"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"അണ്ലോക്ക് ചെയ്യുക"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ഉപകരണം ലോക്ക് ചെയ്തു"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"മുഖം സ്കാൻ ചെയ്യുന്നു"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"സ്ക്രീൻ റെക്കോർഡ്"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ആരംഭിക്കുക"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"നിര്ത്തുക"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ഒറ്റക്കൈ മോഡ്"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ഉപകരണ ക്യാമറയോ മൈക്രോഫോണോ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ഉപയോഗിക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"നിങ്ങളുടെ കാർഡുകൾ ലഭ്യമാക്കുന്നതിൽ ഒരു പ്രശ്നമുണ്ടായി, പിന്നീട് വീണ്ടും ശ്രമിക്കുക"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ലോക്ക് സ്ക്രീൻ ക്രമീകരണം"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR സ്കാൻ ചെയ്യുക"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR കോഡ് സ്കാൻ ചെയ്യാൻ ക്ലിക്ക് ചെയ്യുക"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ഫ്ലൈറ്റ് മോഡ്"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-നുള്ള നിങ്ങളുടെ അടുത്ത അലാറം കേൾക്കില്ല"</string>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index bdbf600..5cfd45a 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ഓഫാണ്"</item>
<item msgid="6866424167599381915">"ഓണാണ്"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"ലഭ്യമല്ല"</item>
+ <item msgid="3301403109049256043">"ഓഫാണ്"</item>
+ <item msgid="8878684975184010135">"ഓണാണ്"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"ലഭ്യമല്ല"</item>
<item msgid="2710157085538036590">"ഓഫാണ്"</item>
<item msgid="7809470840976856149">"ഓണാണ്"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"ലഭ്യമല്ല"</item>
+ <item msgid="146088982397753810">"ഓഫാണ്"</item>
+ <item msgid="460891964396502657">"ഓണാണ്"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 817fc14..f324cc1 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Утас"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Дуут туслах"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Түрийвч"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR код сканнер"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Тайлах"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Төхөөрөмжийг түгжсэн"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скан хийх нүүр царай"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Дэлгэцийн үйлдэл бичих"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Эхлүүлэх"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зогсоох"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг гарын горим"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Төхөөрөмжийн микрофоныг блокоос гаргах уу?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Төхөөрөмжийн камерыг блокоос гаргах уу?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Төхөөрөмжийн камер болон микрофоныг блокоос гаргах уу?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ашиглахын тулд түгжээг тайлах"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Таны картыг авахад асуудал гарлаа. Дараа дахин оролдоно уу"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Түгжигдсэн дэлгэцийн тохиргоо"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-г скан хийх"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодыг скан хийхийн тулд товшино уу"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-т та дараагийн сэрүүлгээ сонсохгүй"</string>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index 81b1b1d..ba14927 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Унтраалттай"</item>
<item msgid="6866424167599381915">"Асаалттай"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Боломжгүй"</item>
+ <item msgid="3301403109049256043">"Унтраалттай"</item>
+ <item msgid="8878684975184010135">"Асаалттай"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Боломжгүй"</item>
<item msgid="2710157085538036590">"Унтраалттай"</item>
<item msgid="7809470840976856149">"Асаалттай"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Боломжгүй"</item>
+ <item msgid="146088982397753810">"Унтраалттай"</item>
+ <item msgid="460891964396502657">"Асаалттай"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index ccbf77b..e72d3c1 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"फोन"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"व्हॉइस सहाय्य"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"वॉलेट"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR कोड स्कॅनर"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"अनलॉक करा"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"डिव्हाइस लॉक केले"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"चेहरा स्कॅन करत आहे"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"स्क्रीन रेकॉर्ड"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"सुरू"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"थांबा"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिव्हाइसचा मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिव्हाइसचा कॅमेरा अनब्लॉक करायचा आहे का?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिव्हाइसचा कॅमेरा आणि मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"वापरण्यासाठी अनलॉक करा"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"तुमची कार्ड मिळवताना समस्या आली, कृपया नंतर पुन्हा प्रयत्न करा"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन सेटिंग्ज"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR स्कॅन करा"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR कोड स्कॅन करण्यासाठी क्लिक करा"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाईल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"विमान मोड"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"तुम्ही तुमचा <xliff:g id="WHEN">%1$s</xliff:g> वाजता होणारा पुढील अलार्म ऐकणार नाही"</string>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index 560194a..dbb7ed5 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"बंद आहे"</item>
<item msgid="6866424167599381915">"सुरू आहे"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"उपलब्ध नाही"</item>
+ <item msgid="3301403109049256043">"बंद आहे"</item>
+ <item msgid="8878684975184010135">"सुरू आहे"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"उपलब्ध नाही"</item>
<item msgid="2710157085538036590">"बंद आहे"</item>
<item msgid="7809470840976856149">"सुरू आहे"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"उपलब्ध नाही"</item>
+ <item msgid="146088982397753810">"बंद आहे"</item>
+ <item msgid="460891964396502657">"सुरू आहे"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 82174ab..09b8241 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Pengambilan tangkapan skrin tidak dibenarkan oleh apl atau organisasi anda"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Edit"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Edit tangkapan skrin"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Kongsi tangkapan skrin"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Tangkap lebih banyak"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ketepikan tangkapan skrin"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string>
@@ -108,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Bantuan Suara"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Dompet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Pengimbas Kod QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Buka kunci"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Peranti dikunci"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Mengimbas wajah"</string>
@@ -294,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rakam skrin"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mula"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Nyahsekat mikrofon peranti?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Nyahsekat kamera peranti?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Nyahsekat kamera dan mikrofon peranti?"</string>
@@ -467,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Terdapat masalah sewaktu mendapatkan kad anda. Sila cuba sebentar lagi"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Tetapan skrin kunci"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Imbas QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik untuk mengimbas kod QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar penggera yang seterusnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index fef4b1d..b3ee999 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Mati"</item>
<item msgid="6866424167599381915">"Hidup"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Tidak tersedia"</item>
+ <item msgid="3301403109049256043">"Mati"</item>
+ <item msgid="8878684975184010135">"Hidup"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Tidak tersedia"</item>
<item msgid="2710157085538036590">"Mati"</item>
<item msgid="7809470840976856149">"Hidup"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Tidak tersedia"</item>
+ <item msgid="146088982397753810">"Mati"</item>
+ <item msgid="460891964396502657">"Hidup"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 281ad88..17fd70e 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"တည်းဖြတ်ရန်"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"ဖန်သားပြင်ဓာတ်ပုံကို တည်းဖြတ်သည်"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"ဖန်သားပြင်ဓာတ်ပုံကို မျှဝေနိုင်သည်"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"နောက်ထပ် ရိုက်ကူးရန်"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ဖန်သားပြင်ဓာတ်ပုံကို ပယ်သည်"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string>
@@ -108,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ဖုန်း"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"အသံ အကူအညီ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR ကုဒ် စကင်ဖတ်စနစ်"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"သော့ဖွင့်ရန်"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"စက်ပစ္စည်းကို လော့ခ်ချထားသည်"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"မျက်နှာ စကင်ဖတ်နေသည်"</string>
@@ -294,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"စကရင် ရိုက်ကူးရန်"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"စတင်ရန်"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ရပ်ရန်"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လက်တစ်ဖက်သုံးမုဒ်"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"စက်၏မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"စက်၏ကင်မရာကို ပြန်ဖွင့်မလား။"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"စက်၏ကင်မရာနှင့် မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
@@ -467,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"သုံးရန် လော့ခ်ဖွင့်ပါ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"သင်၏ကတ်များ ရယူရာတွင် ပြဿနာရှိနေသည်၊ နောက်မှ ထပ်စမ်းကြည့်ပါ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"လော့ခ်မျက်နှာပြင် ဆက်တင်များ"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR စကင်ဖတ်ခြင်း"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ကုဒ် စကင်ဖတ်ရန် ကလစ်နှိပ်ပါ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"အလုပ် ပရိုဖိုင်"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"လေယာဉ်ပျံမုဒ်"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> ၌သင့်နောက်ထပ် နှိုးစက်ကို ကြားမည်မဟုတ်ပါ"</string>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index 898fca3..6c58ac3 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ပိတ်"</item>
<item msgid="6866424167599381915">"ဖွင့်"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"မရနိုင်ပါ"</item>
+ <item msgid="3301403109049256043">"ပိတ်"</item>
+ <item msgid="8878684975184010135">"ဖွင့်"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"မရနိုင်ပါ"</item>
<item msgid="2710157085538036590">"ပိတ်"</item>
<item msgid="7809470840976856149">"ဖွင့်"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"မရနိုင်ပါ"</item>
+ <item msgid="146088982397753810">"ပိတ်"</item>
+ <item msgid="460891964396502657">"ဖွင့်"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 3e5f11d..50a1bdb 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefonnummer"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Talehjelp"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kodeskanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Lås opp"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheten er låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skanning av ansikt"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skjermopptak"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stopp"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du oppheve blokkeringen av enhetsmikrofonen?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du oppheve blokkeringen av enhetskameraet?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du oppheve blokkeringen av enhetskameraet og -mikrofonen?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås opp for å bruke"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Det oppsto et problem med henting av kortene. Prøv igjen senere"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Innstillinger for låseskjermen"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skann QR-kode"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klikk for å skanne en QR-kode"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work-profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flymodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Du hører ikke neste innstilte alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index c0e5b3a..a40e4a4 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Av"</item>
<item msgid="6866424167599381915">"På"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Utilgjengelig"</item>
+ <item msgid="3301403109049256043">"Av"</item>
+ <item msgid="8878684975184010135">"På"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Utilgjengelig"</item>
<item msgid="2710157085538036590">"Av"</item>
<item msgid="7809470840976856149">"På"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Utilgjengelig"</item>
+ <item msgid="146088982397753810">"Av"</item>
+ <item msgid="460891964396502657">"På"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 77cdd51..64881eb 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"फोन"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"आवाज सहायता"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"वालेट"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR कोड स्क्यानर"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"खोल्नुहोस्"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"यन्त्र लक गरिएको छ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"अनुहार स्क्यान गर्दै"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"स्क्रिन रेकर्ड"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"सुरु गर्नुहोस्"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"रोक्नुहोस्"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एक हाते मोड"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"यो वालेट प्रयोग गर्न डिभाइस अनलक गर्नुहोस्"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"तपाईंका कार्डहरू प्राप्त गर्ने क्रममा समस्या भयो, कृपया पछि फेरि प्रयास गर्नुहोस्"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लक स्क्रिनसम्बन्धी सेटिङ"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR स्क्यान गर्नुहोस्"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR कोड स्क्यान गर्न क्लिक गर्नुहोस्"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"तपाईँले आफ्नो अर्को अलार्म <xliff:g id="WHEN">%1$s</xliff:g> सुन्नुहुने छैन"</string>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 571e128..373044d 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"अफ छ"</item>
<item msgid="6866424167599381915">"अन छ"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"उपलब्ध छैन"</item>
+ <item msgid="3301403109049256043">"अफ छ"</item>
+ <item msgid="8878684975184010135">"अन छ"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"उपलब्ध छैन"</item>
<item msgid="2710157085538036590">"अफ छ"</item>
<item msgid="7809470840976856149">"अन छ"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"उपलब्ध छैन"</item>
+ <item msgid="146088982397753810">"अफ छ"</item>
+ <item msgid="460891964396502657">"अन छ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 07e28b6..cb963e6 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -16,9 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog">
- <item name="android:buttonCornerRadius">28dp</item>
- </style>
+ <style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Dialog"/>
<style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" />
@@ -53,13 +51,17 @@
<style name="TextAppearance.InternetDialog.Active">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/connected_network_primary_color</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textDirection">locale</item>
</style>
<style name="TextAppearance.InternetDialog.Secondary.Active">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/connected_network_secondary_color</item>
+ <item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
+ </style>
+
+ <style name="InternetDialog.Divider.Active">
+ <item name="android:background">?android:attr/textColorSecondaryInverse</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 0e8fd0d..093ba95 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefoon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Spraakassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portemonnee"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-codescanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Ontgrendelen"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Apparaat vergrendeld"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Gezicht scannen"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Schermopname"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppen"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Blokkeren van apparaatmicrofoon opheffen?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Blokkeren van apparaatcamera opheffen?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blokkeren van apparaatcamera en -microfoon opheffen?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontgrendelen om te gebruiken"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Er is een probleem opgetreden bij het ophalen van je kaarten. Probeer het later opnieuw."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Instellingen voor vergrendelscherm"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-code scannen"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik om een QR-code te scannen"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Je hoort je volgende wekker niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index 9293f52..6d2ef5f 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Uit"</item>
<item msgid="6866424167599381915">"Aan"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Niet beschikbaar"</item>
+ <item msgid="3301403109049256043">"Uit"</item>
+ <item msgid="8878684975184010135">"Aan"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Niet beschikbaar"</item>
<item msgid="2710157085538036590">"Uit"</item>
<item msgid="7809470840976856149">"Aan"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Niet beschikbaar"</item>
+ <item msgid="146088982397753810">"Uit"</item>
+ <item msgid="460891964396502657">"Aan"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index d02dafb..931d696 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ଫୋନ୍"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ଭଏସ୍ ସହାୟକ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ୱାଲେଟ୍"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR କୋଡ ସ୍କାନର"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ଡିଭାଇସ୍ ଲକ୍ ହୋଇଯାଇଛି"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ଫେସ୍ ସ୍କାନିଙ୍ଗ କରାଯାଉଛି"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ସ୍କ୍ରିନ୍ ରେକର୍ଡ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ଏକ-ହାତ ମୋଡ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ଡିଭାଇସର କ୍ୟାମେରାକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ଡିଭାଇସର କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ବ୍ୟବହାର କରିବାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ଆପଣଙ୍କ କାର୍ଡଗୁଡ଼ିକ ପାଇବାରେ ଏକ ସମସ୍ୟା ହୋଇଥିଲା। ଦୟାକରି ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ସ୍କ୍ରିନ୍ ଲକ୍ ସେଟିଂସ୍"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ସ୍କାନ କରନ୍ତୁ"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"ଏକ QR କୋଡ ସ୍କାନ କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ଏରୋପ୍ଲେନ୍ ମୋଡ୍"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>ବେଳେ ଆପଣ ନିଜର ପରବର୍ତ୍ତୀ ଆଲାର୍ମ ଶୁଣିପାରିବେ ନାହିଁ"</string>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index 848d382..6b52d6e 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ବନ୍ଦ ଅଛି"</item>
<item msgid="6866424167599381915">"ଚାଲୁ ଅଛି"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"ଉପଲବ୍ଧ ନାହିଁ"</item>
+ <item msgid="3301403109049256043">"ବନ୍ଦ ଅଛି"</item>
+ <item msgid="8878684975184010135">"ଚାଲୁ ଅଛି"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"ଉପଲବ୍ଧ ନାହିଁ"</item>
<item msgid="2710157085538036590">"ବନ୍ଦ ଅଛି"</item>
<item msgid="7809470840976856149">"ଚାଲୁ ଅଛି"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"ଉପଲବ୍ଧ ନାହିଁ"</item>
+ <item msgid="146088982397753810">"ବନ୍ଦ ଅଛି"</item>
+ <item msgid="460891964396502657">"ଚାଲୁ ଅଛି"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index f3ea051..d2b2cb5 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ਫ਼ੋਨ ਕਰੋ"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ਵਾਲੇਟ"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR ਕੋਡ ਸਕੈਨਰ"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ਅਣਲਾਕ ਕਰੋ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ਡੀਵਾਈਸ ਲਾਕ ਹੈ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ਚਿਹਰਾ ਸਕੈਨ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ਰੋਕੋ"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ਇੱਕ ਹੱਥ ਮੋਡ"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ਵਰਤਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ਤੁਹਾਡੇ ਕਾਰਡ ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਕੋਈ ਸਮੱਸਿਆ ਆਈ, ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ਲਾਕ ਸਕ੍ਰੀਨ ਸੈਟਿੰਗਾਂ"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ਸਕੈਨ ਕਰੋ"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ਕੋਡ ਨੂੰ ਸਕੈਨ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ਤੁਸੀਂ <xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ ਆਪਣਾ ਅਗਲਾ ਅਲਾਰਮ ਨਹੀਂ ਸੁਣੋਗੇ"</string>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index 409b456..d44add8 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ਬੰਦ ਹੈ"</item>
<item msgid="6866424167599381915">"ਚਾਲੂ ਹੈ"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"ਅਣਉਪਲਬਧ"</item>
+ <item msgid="3301403109049256043">"ਬੰਦ"</item>
+ <item msgid="8878684975184010135">"ਚਾਲੂ"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"ਅਣਉਪਲਬਧ ਹੈ"</item>
<item msgid="2710157085538036590">"ਬੰਦ ਹੈ"</item>
<item msgid="7809470840976856149">"ਚਾਲੂ ਹੈ"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"ਅਣਉਪਲਬਧ"</item>
+ <item msgid="146088982397753810">"ਬੰਦ"</item>
+ <item msgid="460891964396502657">"ਚਾਲੂ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 1532645..0e8b97e 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asystent głosowy"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portfel"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skaner kodów QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Odblokuj"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Urządzenie zablokowane"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skanowanie twarzy"</string>
@@ -297,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Nagrywanie ekranu"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Rozpocznij"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zatrzymaj"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokować mikrofon urządzenia?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokować aparat urządzenia?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokować aparat i mikrofon urządzenia?"</string>
@@ -472,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odblokuj, aby użyć"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Podczas pobierania kart wystąpił problem. Spróbuj ponownie później."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ustawienia ekranu blokady"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanowanie kodu QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknij, aby zeskanować kod QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil służbowy"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Tryb samolotowy"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nie usłyszysz swojego następnego alarmu <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index 2e6df68..4d7ed15 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Wyłączony"</item>
<item msgid="6866424167599381915">"Włączony"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Niedostępny"</item>
+ <item msgid="3301403109049256043">"Wyłączony"</item>
+ <item msgid="8878684975184010135">"Włączony"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Niedostępny"</item>
<item msgid="2710157085538036590">"Wyłączony"</item>
<item msgid="7809470840976856149">"Włączony"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Niedostępny"</item>
+ <item msgid="146088982397753810">"Wyłączony"</item>
+ <item msgid="460891964396502657">"Włączony"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 226220b..90cac8a 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistência de voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de código QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Verificando rosto"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação de tela"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ler código QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index 6647221..ca1ef44 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Desativado"</item>
<item msgid="6866424167599381915">"Ativado"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Indisponível"</item>
+ <item msgid="3301403109049256043">"Desativado"</item>
+ <item msgid="8878684975184010135">"Ativado"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Indisponível"</item>
<item msgid="2710157085538036590">"Desativado"</item>
<item msgid="7809470840976856149">"Ativado"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Indisponível"</item>
+ <item msgid="146088982397753810">"Desativado"</item>
+ <item msgid="460891964396502657">"Ativado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index d98900f..e2d8433 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telemóvel"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistente de voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de códigos QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"A analisar o rosto…"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação ecrã"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Pretende desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Pretende desbloquear a câmara do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Pretende desbloquear a câmara e o microfone?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para utilizar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao obter os seus cartões. Tente novamente mais tarde."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Definições do ecrã de bloqueio"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Leia o QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avião"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Não vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index fc3795a..632db66 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Desligado"</item>
<item msgid="6866424167599381915">"Ligado"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Indisponível"</item>
+ <item msgid="3301403109049256043">"Desligado"</item>
+ <item msgid="8878684975184010135">"Ligado"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Indisponível"</item>
<item msgid="2710157085538036590">"Desligado"</item>
<item msgid="7809470840976856149">"Ligado"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Indisponível"</item>
+ <item msgid="146088982397753810">"Desativado"</item>
+ <item msgid="460891964396502657">"Ativado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 226220b..90cac8a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefone"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistência de voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de código QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Desbloquear"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Verificando rosto"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação de tela"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ler código QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index 6647221..ca1ef44 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Desativado"</item>
<item msgid="6866424167599381915">"Ativado"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Indisponível"</item>
+ <item msgid="3301403109049256043">"Desativado"</item>
+ <item msgid="8878684975184010135">"Ativado"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Indisponível"</item>
<item msgid="2710157085538036590">"Desativado"</item>
<item msgid="7809470840976856149">"Ativado"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Indisponível"</item>
+ <item msgid="146088982397753810">"Desativado"</item>
+ <item msgid="460891964396502657">"Ativado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 9e4c7da..5c89fb1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistent vocal"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Scanner de coduri QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Deblocați"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispozitiv blocat"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanarea chipului"</string>
@@ -295,6 +294,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Înregistrarea ecranului"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Începeți"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Opriți"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblocați microfonul dispozitivului?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblocați camera dispozitivului?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblocați camera și microfonul dispozitivului?"</string>
@@ -469,10 +469,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Deblocați pentru a folosi"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"A apărut o problemă la preluarea cardurilor. Încercați din nou mai târziu"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setările ecranului de blocare"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scanați un cod QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Dați clic pentru a scana un cod QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nu veți auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index 53d5fa2..eea69f8 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Dezactivat"</item>
<item msgid="6866424167599381915">"Activat"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Indisponibil"</item>
+ <item msgid="3301403109049256043">"Dezactivat"</item>
+ <item msgid="8878684975184010135">"Activat"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Indisponibilă"</item>
<item msgid="2710157085538036590">"Dezactivată"</item>
<item msgid="7809470840976856149">"Activată"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Indisponibil"</item>
+ <item msgid="146088982397753810">"Dezactivat"</item>
+ <item msgid="460891964396502657">"Activat"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 66e32f2..ecff557 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Изменить"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Изменить скриншот"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Поделиться скриншотом"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Увеличить площадь скриншота"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Закрыть скриншот"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string>
@@ -108,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон."</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Аудиоподсказки"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Кошелек"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Сканер QR-кодов"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Разблокировать."</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Устройство заблокировано"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканирование лица"</string>
@@ -127,7 +125,7 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Подтверждено"</string>
<string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Нажмите \"Подтвердить\""</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"Аутентификация выполнена"</string>
- <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Использовать PIN-код"</string>
+ <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"PIN-код"</string>
<string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Использовать графический ключ"</string>
<string name="biometric_dialog_use_password" msgid="3445033859393474779">"Использовать пароль"</string>
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"Неверный PIN-код."</string>
@@ -298,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запись видео с экрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Начать"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Остановить"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблокировать камеру и микрофон устройства?"</string>
@@ -473,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблокировать для использования"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Не удалось получить информацию о картах. Повторите попытку позже."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки заблокированного экрана"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканер QR-кодов"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Нажмите, чтобы отсканировать QR-код."</string>
<string name="status_bar_work" msgid="5238641949837091056">"Рабочий профиль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим полета"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Следующий будильник: <xliff:g id="WHEN">%1$s</xliff:g>. Звук отключен."</string>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index 14098fc..6bc486b 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Откл."</item>
<item msgid="6866424167599381915">"Вкл."</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Недоступен"</item>
+ <item msgid="3301403109049256043">"Отключен"</item>
+ <item msgid="8878684975184010135">"Включен"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Функция недоступна"</item>
<item msgid="2710157085538036590">"Откл."</item>
<item msgid="7809470840976856149">"Вкл."</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Недоступен"</item>
+ <item msgid="146088982397753810">"Отключен"</item>
+ <item msgid="460891964396502657">"Включен"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index a25672c..b24db63 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"දුරකථනය"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"හඬ සහාය"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"පසුම්බිය"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR කේත ස්කෑනරය"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"අඟුල අරින්න"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"උපාංගය අගුලු දමා ඇත"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"මුහුණ ස්කෑන් කිරීම"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"තිර පටිගත කිරීම"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ආරම්භ කරන්න"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"නතර කරන්න"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් ප්රකාරය"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"භාවිත කිරීමට අගුලු හරින්න"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ඔබගේ කාඩ්පත ලබා ගැනීමේ ගැටලුවක් විය, කරුණාකර පසුව නැවත උත්සාහ කරන්න"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"අගුලු තිර සැකසීම්"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR කේතය ස්කෑන් කරන්න"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR කේතයක් ස්කෑන් කිරීමට ක්ලික් කරන්න"</string>
<string name="status_bar_work" msgid="5238641949837091056">"කාර්යාල පැතිකඩ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ගුවන්යානා ප්රකාරය"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ඔබට ඔබේ ඊළඟ එලාමය <xliff:g id="WHEN">%1$s</xliff:g> නොඇසෙනු ඇත"</string>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index ed39e4a..9445457 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"අක්රියයි"</item>
<item msgid="6866424167599381915">"සක්රියයි"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"නොමැත"</item>
+ <item msgid="3301403109049256043">"ක්රියාවිරහිතයි"</item>
+ <item msgid="8878684975184010135">"ක්රියාත්මකයි"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"නොමැත"</item>
<item msgid="2710157085538036590">"අක්රියයි"</item>
<item msgid="7809470840976856149">"සක්රියයි"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"නොමැත"</item>
+ <item msgid="146088982397753810">"ක්රියාවිරහිතයි"</item>
+ <item msgid="460891964396502657">"ක්රියාත්මකයි"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 4c672bf..5ab4026 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefón"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hlasový asistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Peňaženka"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skener QR kódov"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Odomknúť"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Zariadenie je uzamknuté"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skenovanie tváre"</string>
@@ -297,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekordér obrazovky"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začať"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončiť"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednej ruky"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Chcete odblokovať mikrofón zariadenia?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Chcete odblokovať fotoaparát zariadenia?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Chcete odblokovať fotoaparát a mikrofón zariadenia?"</string>
@@ -472,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odomknúť a použiť"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri načítavaní kariet sa vyskytol problém. Skúste to neskôr."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavenia uzamknutej obrazovky"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skenovanie QR kódu"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknutím naskenujte QR kód"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Váš budík o <xliff:g id="WHEN">%1$s</xliff:g> sa nespustí"</string>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index 817e8fb..b7d37c8 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Vypnuté"</item>
<item msgid="6866424167599381915">"Zapnuté"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nie je k dispozícii"</item>
+ <item msgid="3301403109049256043">"Vypnutý"</item>
+ <item msgid="8878684975184010135">"Zapnutý"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nie je k dispozícii"</item>
<item msgid="2710157085538036590">"Vypnuté"</item>
<item msgid="7809470840976856149">"Zapnuté"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nedostupné"</item>
+ <item msgid="146088982397753810">"Vypnuté"</item>
+ <item msgid="460891964396502657">"Zapnuté"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 43df9e7..f00bf38 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovni pomočnik"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Google Denarnica"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Optični bralnik kod QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Odkleni"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Naprava je zaklenjena."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Optično branje obraza"</string>
@@ -297,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snemanje zaslona"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začni"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ustavi"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite odblokirati mikrofon v napravi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite odblokirati fotoaparat v napravi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite odblokirati fotoaparat in mikrofon v napravi?"</string>
@@ -472,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odklenite za uporabo"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri pridobivanju kartic je prišlo do težave. Poskusite znova pozneje."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavitve zaklepanja zaslona"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Optično branje kode QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite, če želite optično prebrati kodo QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za Android Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način za letalo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Naslednjega alarma ob <xliff:g id="WHEN">%1$s</xliff:g> ne boste slišali"</string>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index 6f6a8f1..28e3917 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Izklopljeno"</item>
<item msgid="6866424167599381915">"Vklopljeno"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Ni na voljo"</item>
+ <item msgid="3301403109049256043">"Izklopljeno"</item>
+ <item msgid="8878684975184010135">"Vklopljeno"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Ni na voljo"</item>
<item msgid="2710157085538036590">"Izklopljeno"</item>
<item msgid="7809470840976856149">"Vklopljeno"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Ni na voljo"</item>
+ <item msgid="146088982397753810">"Izklopljeno"</item>
+ <item msgid="460891964396502657">"Vklopljeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index bc9018772..413bcec 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Modifiko"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Modifiko pamjen e ekranit"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Ndaj pamjen e ekranit"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Regjistro më shumë"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Hiq pamjen e ekranit"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string>
@@ -108,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefoni"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ndihma zanore"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skaneri i kodeve QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Shkyç"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Pajisja është e kyçur"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Po skanon fytyrën"</string>
@@ -294,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Regjistrimi i ekranit"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Nis"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ndalo"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Të zhbllokohet mikrofoni i pajisjes?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Të zhbllokohet kamera e pajisjes?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Të zhbllokohen kamera dhe mikrofoni i pajisjes?"</string>
@@ -467,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Shkyçe për ta përdorur"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pati një problem me marrjen e kartave të tua. Provo përsëri më vonë"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cilësimet e ekranit të kyçjes"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skano kodin QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliko për të skanuar një kod QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profili i punës"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modaliteti i aeroplanit"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nuk do ta dëgjosh alarmin e radhës në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index a88c530..6643e04 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Joaktiv"</item>
<item msgid="6866424167599381915">"Aktiv"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Nuk ofrohet"</item>
+ <item msgid="3301403109049256043">"Joaktiv"</item>
+ <item msgid="8878684975184010135">"Aktiv"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Nuk ofrohet"</item>
<item msgid="2710157085538036590">"Joaktiv"</item>
<item msgid="7809470840976856149">"Aktiv"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Nuk ofrohet"</item>
+ <item msgid="146088982397753810">"Joaktiv"</item>
+ <item msgid="460891964396502657">"Aktiv"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 0606d0e..b5cd94b 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Телефон"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласовна помоћ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Новчаник"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Скенер QR кода"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Откључајте"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Уређај је закључан"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скенирање лица"</string>
@@ -295,6 +294,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Снимање екрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Почните"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зауставите"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим једном руком"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Желите да одблокирате микрофон уређаја?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Желите да одблокирате камеру уређаја?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Желите да одблокирате камеру и микрофон уређаја?"</string>
@@ -469,10 +469,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Откључај ради коришћења"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Дошло је до проблема при преузимању картица. Пробајте поново касније"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Подешавања закључаног екрана"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Скенирај QR кôд"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликните да бисте скенирали QR кôд"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Пословни профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим рада у авиону"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Нећете чути следећи аларм у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -675,7 +673,7 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Неке функције су ограничене док се телефон не охлади.\nДодирните за више информација"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефон ће аутоматски покушати да се охлади. И даље ћете моћи да користите телефон, али ће спорије реаговати.\n\nКада се телефон охлади, нормално ће радити."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Погледајте упозорења"</string>
- <string name="high_temp_alarm_title" msgid="2359958549570161495">"Искључите пуњач из напајања"</string>
+ <string name="high_temp_alarm_title" msgid="2359958549570161495">"Искључите пуњач из струје"</string>
<string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"Дошло је до проблема са пуњењем овог уређаја. Искључите адаптер из напајања и будите пажљиви јер кабл може да буде топао."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Погледајте упозорења"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Лева пречица"</string>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index e2f9c62..63542da 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Искључено"</item>
<item msgid="6866424167599381915">"Укључено"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Недоступно"</item>
+ <item msgid="3301403109049256043">"Искључено"</item>
+ <item msgid="8878684975184010135">"Укључено"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Недоступно"</item>
<item msgid="2710157085538036590">"Искључено"</item>
<item msgid="7809470840976856149">"Укључено"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Недоступно"</item>
+ <item msgid="146088982397753810">"Искључено"</item>
+ <item msgid="460891964396502657">"Укључено"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 60b0097..1bae752 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Mobil"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Röstassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-skanner"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Lås upp"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheten är låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Registrerar ansikte"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skärminspelning"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starta"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppa"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vill du återaktivera enhetens mikrofon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vill du återaktivera enhetens kamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vill du återaktivera enhetens kamera och mikrofon?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås upp för att använda"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Det gick inte att hämta dina kort. Försök igen senare."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Inställningar för låsskärm"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanna QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klicka för att skanna en QR-kod"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Jobbprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flygplansläge"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nästa alarm, kl. <xliff:g id="WHEN">%1$s</xliff:g>, kommer inte att höras"</string>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index a7ba12b..e009e0b 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Av"</item>
<item msgid="6866424167599381915">"På"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Inte tillgängligt"</item>
+ <item msgid="3301403109049256043">"Av"</item>
+ <item msgid="8878684975184010135">"På"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Inte tillgängligt"</item>
<item msgid="2710157085538036590">"Av"</item>
<item msgid="7809470840976856149">"På"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Inte tillgängligt"</item>
+ <item msgid="146088982397753810">"Av"</item>
+ <item msgid="460891964396502657">"På"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 22e138f..c4e9540 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Simu"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Mapendekezo ya Sauti"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Kichanganuzi cha Msimbo wa QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Fungua"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Kifaa kimefungwa"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Inachanganua uso"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekodi ya skrini"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Anza kurekodi"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Acha kurekodi"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Ungependa kuwacha kuzuia maikrofoni ya kifaa?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Ungependa kuwacha kuzuia kamera ya kifaa?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Ungependa kuwacha kuzuia kamera na maikrofoni ya kifaa?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Fungua ili utumie"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hitilafu imetokea wakati wa kuleta kadi zako, tafadhali jaribu tena baadaye"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mipangilio ya kufunga skrini"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Changanua QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Bofya ili uchanganue msimbo wa QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Hutasikia kengele yako inayofuata ya saa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index f1fbf38..beabdc4 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Kimezimwa"</item>
<item msgid="6866424167599381915">"Kimewashwa"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Hakipatikani"</item>
+ <item msgid="3301403109049256043">"Kimezimwa"</item>
+ <item msgid="8878684975184010135">"Kimewashwa"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Hakipatikani"</item>
<item msgid="2710157085538036590">"Kimezimwa"</item>
<item msgid="7809470840976856149">"Kimewashwa"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Haipatikani"</item>
+ <item msgid="146088982397753810">"Imezimwa"</item>
+ <item msgid="460891964396502657">"Imewashwa"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index c2a3ed4..f8af3a6 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ஃபோன்"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"குரல் உதவி"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"வாலட்"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR குறியீடு ஸ்கேனர்"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"அன்லாக் செய்"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"சாதனம் பூட்டப்பட்டுள்ளது"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"முகத்தை ஸ்கேன் செய்கிறது"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"ஸ்கிரீன் ரெக்கார்டு"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"தொடங்கு"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"நிறுத்து"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ஒற்றைக் கைப் பயன்முறை"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"சாதனத்தின் மைக்ரோஃபோனுக்கான தடுப்பை நீக்கவா?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"சாதனத்தின் கேமராவுக்கான தடுப்பை நீக்கவா?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"சாதனத்தின் கேமராவுக்கும் மைக்ரோஃபோனுக்குமான தடுப்பை நீக்கவா?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"பயன்படுத்துவதற்கு அன்லாக் செய்க"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"உங்கள் கார்டுகளின் விவரங்களைப் பெறுவதில் சிக்கல் ஏற்பட்டது, பிறகு முயலவும்"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"பூட்டுத் திரை அமைப்புகள்"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR குறியீட்டை ஸ்கேன் செய்யுங்கள்"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR குறியீட்டை ஸ்கேன் செய்யக் கிளிக் செய்யவும்"</string>
<string name="status_bar_work" msgid="5238641949837091056">"பணிக் கணக்கு"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"விமானப் பயன்முறை"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"அடுத்த அலாரத்தை <xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு கேட்க மாட்டீர்கள்"</string>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index b2cc840..9f2a2e9 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"முடக்கப்பட்டுள்ளது"</item>
<item msgid="6866424167599381915">"இயக்கப்பட்டுள்ளது"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"கிடைக்கவில்லை"</item>
+ <item msgid="3301403109049256043">"இயக்கு"</item>
+ <item msgid="8878684975184010135">"முடக்கு"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"கிடைக்கவில்லை"</item>
<item msgid="2710157085538036590">"முடக்கப்பட்டுள்ளது"</item>
<item msgid="7809470840976856149">"இயக்கப்பட்டுள்ளது"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"கிடைக்கவில்லை"</item>
+ <item msgid="146088982397753810">"முடக்கப்பட்டுள்ளது"</item>
+ <item msgid="460891964396502657">"இயக்கப்பட்டுள்ளது"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index d8a4c21..77d076d 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"ఫోన్"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"వాయిస్ అసిస్టెంట్"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"వాలెట్"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR కోడ్ స్కానర్"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"అన్లాక్ చేయి"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"పరికరం లాక్ చేయబడింది"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ముఖాన్ని స్కాన్ చేస్తోంది"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"స్క్రీన్ రికార్డ్"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ప్రారంభించు"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ఆపు"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్ను అన్బ్లాక్ చేయమంటారా?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్బ్లాక్ చేయమంటారా?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్లను అన్బ్లాక్ చేయమంటారా?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ఉపయోగించడానికి అన్లాక్ చేయండి"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"మీ కార్డ్లను పొందడంలో సమస్య ఉంది, దయచేసి తర్వాత మళ్లీ ట్రై చేయండి"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"లాక్ స్క్రీన్ సెట్టింగ్లు"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QRను స్కాన్ చేయండి"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR కోడ్ను స్కాన్ చేయడానికి క్లిక్ చేయండి"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ఆఫీస్ ప్రొఫైల్"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ఎయిర్ప్లేన్ మోడ్"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"మీరు <xliff:g id="WHEN">%1$s</xliff:g> సెట్ చేసిన మీ తర్వాత అలారం మీకు వినిపించదు"</string>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index 3a2dca0..c23436f 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ఆఫ్లో ఉంది"</item>
<item msgid="6866424167599381915">"ఆన్లో ఉంది"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"అందుబాటులో లేదు"</item>
+ <item msgid="3301403109049256043">"ఆఫ్లో ఉంది"</item>
+ <item msgid="8878684975184010135">"ఆన్లో ఉంది"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"అందుబాటులో లేదు"</item>
<item msgid="2710157085538036590">"ఆఫ్లో ఉంది"</item>
<item msgid="7809470840976856149">"ఆన్లో ఉంది"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"అందుబాటులో లేదు"</item>
+ <item msgid="146088982397753810">"ఆఫ్"</item>
+ <item msgid="460891964396502657">"ఆన్"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 5922678..15215e3 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"แก้ไข"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"แก้ไขภาพหน้าจอ"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"แชร์ภาพหน้าจอ"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"จับภาพได้มากขึ้น"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ปิดภาพหน้าจอ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string>
@@ -108,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"โทรศัพท์"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ตัวช่วยเสียง"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"เครื่องมือสแกนคิวอาร์โค้ด"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"ปลดล็อก"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"อุปกรณ์ถูกล็อก"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"กำลังสแกนใบหน้า"</string>
@@ -294,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"บันทึกหน้าจอ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"เริ่ม"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"หยุด"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"โหมดมือเดียว"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"เลิกบล็อกไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"เลิกบล็อกกล้องของอุปกรณ์ใช่ไหม"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"เลิกบล็อกกล้องและไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
@@ -467,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ปลดล็อกเพื่อใช้"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"เกิดปัญหาในการดึงข้อมูลบัตรของคุณ โปรดลองอีกครั้งในภายหลัง"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"การตั้งค่าหน้าจอล็อก"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"สแกนคิวอาร์"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"คลิกเพื่อสแกนคิวอาร์โค้ด"</string>
<string name="status_bar_work" msgid="5238641949837091056">"โปรไฟล์งาน"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"โหมดบนเครื่องบิน"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"คุณจะไม่ได้ยินเสียงปลุกครั้งถัดไปในเวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index 170a9be..850fb7c 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"ปิด"</item>
<item msgid="6866424167599381915">"เปิด"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"ไม่พร้อมใช้งาน"</item>
+ <item msgid="3301403109049256043">"ปิด"</item>
+ <item msgid="8878684975184010135">"เปิด"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"ไม่พร้อมใช้งาน"</item>
<item msgid="2710157085538036590">"ปิด"</item>
<item msgid="7809470840976856149">"เปิด"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"ไม่มีจำหน่าย"</item>
+ <item msgid="146088982397753810">"ปิด"</item>
+ <item msgid="460891964396502657">"เปิด"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 05efac2..a944124 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -69,8 +69,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Hindi pinahihintulutan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"I-edit"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"I-edit ang screenshot"</string>
- <!-- no translation found for screenshot_share_description (2861628935812656612) -->
- <skip />
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Ibahagi ang screenshot"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Mag-capture pa"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"I-dismiss ang screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string>
@@ -108,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telepono"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Scanner ng QR Code"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"I-unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Naka-lock ang device"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Sina-scan ang mukha"</string>
@@ -294,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Pag-record ng screen"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Magsimula"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ihinto"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"I-unblock ang mikropono ng device?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"I-unblock ang camera ng device?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"I-unblock ang camera at mikropono ng device?"</string>
@@ -467,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"I-unlock para magamit"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Nagkaproblema sa pagkuha ng iyong mga card, pakisubukan ulit sa ibang pagkakataon"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mga setting ng lock screen"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"I-scan ang QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Mag-click para mag-scan ng QR code"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profile sa trabaho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Hindi mo maririnig ang iyong susunod na alarm ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index 6935782..dbf54ec 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Naka-off"</item>
<item msgid="6866424167599381915">"Naka-on"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Hindi Available"</item>
+ <item msgid="3301403109049256043">"Naka-off"</item>
+ <item msgid="8878684975184010135">"Naka-on"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Hindi available"</item>
<item msgid="2710157085538036590">"Naka-off"</item>
<item msgid="7809470840976856149">"Naka-on"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Hindi available"</item>
+ <item msgid="146088982397753810">"Naka-off"</item>
+ <item msgid="460891964396502657">"Naka-on"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 87803f3..98b15d9 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Sesli Yardım"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Cüzdan"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR kodu tarayıcı"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Kilidi aç"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Cihaz kilitlendi"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Yüz taranıyor"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran kaydı"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Başlat"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Durdur"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonunun engellemesi kaldırılsın mı?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerasının engellemesi kaldırılsın mı?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası ile mikrofonunun engellemesi kaldırılsın mı?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Kullanmak için kilidi aç"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kartlarınız alınırken bir sorun oluştu. Lütfen daha sonra tekrar deneyin"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilit ekranı ayarları"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodunu tarayın"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodu taramak için tıklayın"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Uçak modu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> olarak ayarlanmış bir sonraki alarmınızı duymayacaksınız"</string>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index 34179b5..9eded7c 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Kapalı"</item>
<item msgid="6866424167599381915">"Açık"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Kullanılamıyor"</item>
+ <item msgid="3301403109049256043">"Kapalı"</item>
+ <item msgid="8878684975184010135">"Açık"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Kullanılamıyor"</item>
<item msgid="2710157085538036590">"Kapalı"</item>
<item msgid="7809470840976856149">"Açık"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Bilinmiyor"</item>
+ <item msgid="146088982397753810">"Kapalı"</item>
+ <item msgid="460891964396502657">"Açık"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 0dbb0db..6535ecb 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Номер телефону"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Голосові підказки"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Гаманець"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Сканер QR-коду"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Розблокувати"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Пристрій заблоковано"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканування обличчя"</string>
@@ -297,6 +296,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запис екрана"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Почати"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зупинити"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Надати доступ до мікрофона?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Надати доступ до камери пристрою?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Надати доступ до камери й мікрофона?"</string>
@@ -472,10 +472,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Розблокувати, щоб використовувати"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Не вдалось отримати ваші картки. Повторіть спробу пізніше."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Параметри блокування екрана"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканувати QR-код"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Натисніть, щоб відсканувати QR-код"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Робочий профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим польоту"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Наступний сигнал о <xliff:g id="WHEN">%1$s</xliff:g> не пролунає"</string>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 21e0128..c9da2b4 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Вимкнено"</item>
<item msgid="6866424167599381915">"Увімкнено"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Недоступно"</item>
+ <item msgid="3301403109049256043">"Вимкнено"</item>
+ <item msgid="8878684975184010135">"Увімкнено"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Недоступно"</item>
<item msgid="2710157085538036590">"Вимкнено"</item>
<item msgid="7809470840976856149">"Увімкнено"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Недоступно"</item>
+ <item msgid="146088982397753810">"Вимкнено"</item>
+ <item msgid="460891964396502657">"Увімкнено"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 1d4b0b0..0c46c17 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"فون"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"صوتی معاون"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR کوڈ اسکینر"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"غیر مقفل کریں"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"آلہ مقفل کر دیا گیا"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"اسکیننگ چہرہ"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"اسکرین ریکارڈ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"شروع کریں"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"روکیں"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ایک ہاتھ کی وضع"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"آلے کا مائیکروفون غیر مسدود کریں؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"آلے کا کیمرا غیر مسدود کریں؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"آلے کا کیمرا اور مائیکروفون غیر مسدود کریں؟"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"استعمال کرنے کے لیے غیر مقفل کریں"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"آپ کے کارڈز حاصل کرنے میں ایک مسئلہ درپیش تھا، براہ کرم بعد میں دوبارہ کوشش کریں"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"مقفل اسکرین کی ترتیبات"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR اسکین کریں"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR کوڈ اسکین کرنے کے لیے کلک کریں"</string>
<string name="status_bar_work" msgid="5238641949837091056">"دفتری پروفائل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ہوائی جہاز وضع"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"آپ کو <xliff:g id="WHEN">%1$s</xliff:g> بجے اپنا اگلا الارم سنائی نہیں دے گا"</string>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index 71f2a08..217d445 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"آف ہے"</item>
<item msgid="6866424167599381915">"آن ہے"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"غیر دستیاب"</item>
+ <item msgid="3301403109049256043">"آف"</item>
+ <item msgid="8878684975184010135">"آن"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"دستیاب نہیں ہے"</item>
<item msgid="2710157085538036590">"آف ہے"</item>
<item msgid="7809470840976856149">"آن ہے"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"غیر دستیاب"</item>
+ <item msgid="146088982397753810">"آف"</item>
+ <item msgid="460891964396502657">"آن"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index eb02d15..8ef5264 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ovozli yordam"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR kod skaneri"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Qulfdan chiqarish"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Qurilma qulflandi"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Yuzni skanerlash"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran yozuvi"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Boshlash"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Toʻxtatish"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Qurilma mikrofoni blokdan chiqarilsinmi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Qurilma kamerasi blokdan chiqarilsinmi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Qurilma kamerasi va mikrofoni blokdan chiqarilsinmi?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Foydalanish uchun qulfdan chiqarish"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Bildirgilarni yuklashda xatolik yuz berdi, keyinroq qaytadan urining"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Qulflangan ekran sozlamalari"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodni skanerlash"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodni skanerlash uchun bosing"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ish profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Parvoz rejimi"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Keyingi signal (<xliff:g id="WHEN">%1$s</xliff:g>) chalinmaydi"</string>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index f69166e..0fd077c 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Oʻchiq"</item>
<item msgid="6866424167599381915">"Yoniq"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Ishlamaydi"</item>
+ <item msgid="3301403109049256043">"Oʻchiq"</item>
+ <item msgid="8878684975184010135">"Yoniq"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Ishlamaydi"</item>
<item msgid="2710157085538036590">"Oʻchiq"</item>
<item msgid="7809470840976856149">"Yoniq"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Ishlamaydi"</item>
+ <item msgid="146088982397753810">"Oʻchiq"</item>
+ <item msgid="460891964396502657">"Yoniq"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 5444f4c..245defb 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Điện thoại"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Trợ lý thoại"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Ví"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Trình quét mã QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Mở khóa"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Đã khóa thiết bị"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Quét tìm khuôn mặt"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ghi màn hình"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Bắt đầu"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dừng"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Chế độ một tay"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Bỏ chặn micrô của thiết bị?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Bỏ chặn máy ảnh của thiết bị?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Bỏ chặn máy ảnh và micrô của thiết bị?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Mở khóa để sử dụng"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Đã xảy ra sự cố khi tải thẻ của bạn. Vui lòng thử lại sau"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cài đặt màn hình khóa"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Quét mã QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Nhấp để quét mã QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ máy bay"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy báo thức tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index a973ffc..72ffc6d 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Đang tắt"</item>
<item msgid="6866424167599381915">"Đang bật"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Không dùng được"</item>
+ <item msgid="3301403109049256043">"Đang tắt"</item>
+ <item msgid="8878684975184010135">"Đang bật"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Không hoạt động"</item>
<item msgid="2710157085538036590">"Đang tắt"</item>
<item msgid="7809470840976856149">"Đang bật"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Không hoạt động"</item>
+ <item msgid="146088982397753810">"Đang tắt"</item>
+ <item msgid="460891964396502657">"Đang bật"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index e54fa0d..2e9ba10 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"电话"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"语音助理"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"电子钱包"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"二维码扫描器"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"解锁"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"设备已锁定"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"正在扫描面孔"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"屏幕录制"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"开始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"单手模式"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解锁设备麦克风吗?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解锁设备摄像头吗?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解锁设备摄像头和麦克风吗?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解锁设备即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"获取您的卡片时出现问题,请稍后重试"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"锁定屏幕设置"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"扫描二维码"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"点击即可扫描二维码"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作资料"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飞行模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"您在<xliff:g id="WHEN">%1$s</xliff:g>将不会听到下次闹钟响铃"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index e4a6dcd..7912813 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"已关闭"</item>
<item msgid="6866424167599381915">"已开启"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"不可用"</item>
+ <item msgid="3301403109049256043">"关闭"</item>
+ <item msgid="8878684975184010135">"开启"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"不可用"</item>
<item msgid="2710157085538036590">"已关闭"</item>
<item msgid="7809470840976856149">"已开启"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"不可用"</item>
+ <item msgid="146088982397753810">"关闭"</item>
+ <item msgid="460891964396502657">"开启"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index d791554..7550a73 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"電話"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"語音助手"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"電子錢包"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR 碼掃瞄器"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"解鎖"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"裝置已上鎖"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"掃瞄緊面孔"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"螢幕錄影"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解除封鎖裝置相機和麥克風嗎?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"擷取資訊卡時發生問題,請稍後再試。"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"上鎖畫面設定"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"掃瞄 QR 碼"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"按一下即可掃瞄 QR 碼"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作設定檔"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛行模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"您不會<xliff:g id="WHEN">%1$s</xliff:g>聽到鬧鐘"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index 4e6af22..5f1b350 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"已關閉"</item>
<item msgid="6866424167599381915">"已開啟"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"無法使用"</item>
+ <item msgid="3301403109049256043">"已關閉"</item>
+ <item msgid="8878684975184010135">"已開啟"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"無法使用"</item>
<item msgid="2710157085538036590">"已關閉"</item>
<item msgid="7809470840976856149">"已開啟"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"未有提供"</item>
+ <item msgid="146088982397753810">"關閉"</item>
+ <item msgid="460891964396502657">"開啟"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 628e4d4..a771098 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"電話"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"語音小幫手"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"電子錢包"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR 圖碼掃描器"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"解除鎖定"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"裝置已鎖定"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"掃描臉孔"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"螢幕錄影"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要將裝置麥克風解除封鎖嗎?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要將裝置相機解除封鎖嗎?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"擷取卡片時發生問題,請稍後再試"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"螢幕鎖定設定"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"掃描 QR 圖碼"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"按一下即可掃描 QR 圖碼"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作資料夾"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛航模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"你不會聽到下一個<xliff:g id="WHEN">%1$s</xliff:g> 的鬧鐘"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 4e6af22..3d81fc8 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"已關閉"</item>
<item msgid="6866424167599381915">"已開啟"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"無法使用"</item>
+ <item msgid="3301403109049256043">"已關閉"</item>
+ <item msgid="8878684975184010135">"已開啟"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"無法使用"</item>
<item msgid="2710157085538036590">"已關閉"</item>
<item msgid="7809470840976856149">"已開啟"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"無法使用"</item>
+ <item msgid="146088982397753810">"已關閉"</item>
+ <item msgid="460891964396502657">"已開啟"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index cea362b..64920b2 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -107,8 +107,7 @@
<string name="accessibility_phone_button" msgid="4256353121703100427">"Ifoni"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Isisekeli sezwi"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"I-wallet"</string>
- <!-- no translation found for accessibility_qr_code_scanner_button (7521277927692910795) -->
- <skip />
+ <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Iskena sekhodi ye-QR"</string>
<string name="accessibility_unlock_button" msgid="122785427241471085">"Vula"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Idivayisi ikhiyiwe"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Ukuskena ubuso"</string>
@@ -293,6 +292,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Irekhodi lesikrini"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Qala"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Misa"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vulela imakrofoni yedivayisi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vulela ikhamera yedivayisi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vulela ikhamera yedivayisi nemakrofoni?"</string>
@@ -466,10 +466,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Vula ukuze usebenzise"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kube khona inkinga yokuthola amakhadi akho, sicela uzame futhi ngemuva kwesikhathi"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Amasethingi okukhiya isikrini"</string>
- <!-- no translation found for qr_code_scanner_title (1598912458255252498) -->
- <skip />
- <!-- no translation found for qr_code_scanner_description (7452098243938659945) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skena i-QR"</string>
+ <string name="qr_code_scanner_description" msgid="7452098243938659945">"Chofoza ukuze uskene ikhodi ye-QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ngeke uzwe i-alamu yakho elandelayo ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index 201aa10..a124c9e 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -151,12 +151,19 @@
<item msgid="7571394439974244289">"Valiwe"</item>
<item msgid="6866424167599381915">"Vuliwe"</item>
</string-array>
- <!-- no translation found for tile_states_qr_code_scanner:0 (7435143266149257618) -->
- <!-- no translation found for tile_states_qr_code_scanner:1 (3301403109049256043) -->
- <!-- no translation found for tile_states_qr_code_scanner:2 (8878684975184010135) -->
+ <string-array name="tile_states_qr_code_scanner">
+ <item msgid="7435143266149257618">"Akutholakali"</item>
+ <item msgid="3301403109049256043">"Valiwe"</item>
+ <item msgid="8878684975184010135">"Vuliwe"</item>
+ </string-array>
<string-array name="tile_states_alarm">
<item msgid="4936533380177298776">"Akutholakali"</item>
<item msgid="2710157085538036590">"Valiwe"</item>
<item msgid="7809470840976856149">"Vuliwe"</item>
</string-array>
+ <string-array name="tile_states_onehanded">
+ <item msgid="8189342855739930015">"Akutholakali"</item>
+ <item msgid="146088982397753810">"Valiwe"</item>
+ <item msgid="460891964396502657">"Vuliwe"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c5b47d0..2b16ec2 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -16,7 +16,8 @@
* limitations under the License.
*/
-->
-<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<drawable name="notification_number_text_color">#ffffffff</drawable>
<drawable name="system_bar_background">@color/system_bar_background_opaque</drawable>
<color name="system_bar_background_opaque">#ff000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 56464e4..b3bbd87 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -82,7 +82,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,fgsmanager
</string>
<!-- The tiles to display in QuickSettings -->
@@ -597,6 +597,17 @@
280
</integer>
+ <!-- Haptic feedback intensity for ticks used for the udfps dwell time -->
+ <item name="config_udfpsTickIntensity" translatable="false" format="float"
+ type="dimen">.5</item>
+
+ <!-- Haptic feedback delay between ticks used for udfps dwell time -->
+ <integer name="config_udfpsTickDelay" translatable="false">25</integer>
+
+ <!-- Haptic feedback tick type - if true, uses VibrationEffect.Composition.PRIMITIVE_LOW_TICK
+ else uses VibrationEffect.Composition.PRIMITIVE_TICK -->
+ <bool name="config_udfpsUseLowTick">true</bool>
+
<!-- package name of a built-in camera app to use to restrict implicit intent resolution
when the double-press power gesture is used. Ignored if empty. -->
<string translatable="false" name="config_cameraGesturePackage"></string>
@@ -671,6 +682,9 @@
attempts. -->
<integer name="config_communalSourceReconnectBaseDelay">1000</integer>
+ <!-- The minimum time in milliseconds for a connection to be considered connected. Any time -->
+ <integer name="config_connectionMinDuration">1000</integer>
+
<!-- Flag to activate notification to contents feature -->
<bool name="config_notificationToContents">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 059aad7..a437ae6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1085,11 +1085,13 @@
<!-- Output switcher panel related dimensions -->
<dimen name="media_output_dialog_list_margin">12dp</dimen>
<dimen name="media_output_dialog_list_max_height">364dp</dimen>
- <dimen name="media_output_dialog_header_album_icon_size">48dp</dimen>
+ <dimen name="media_output_dialog_header_album_icon_size">72dp</dimen>
<dimen name="media_output_dialog_header_back_icon_size">32dp</dimen>
<dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
- <dimen name="media_output_dialog_icon_corner_radius">8dp</dimen>
+ <dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
+ <dimen name="media_output_dialog_button_padding_horizontal">16dp</dimen>
+ <dimen name="media_output_dialog_button_padding_vertical">8dp</dimen>
<!-- Distance that the full shade transition takes in order for qs to fully transition to the
shade -->
@@ -1240,6 +1242,8 @@
<!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_max_height">662dp</dimen>
+ <!-- The height of the WiFi network in Internet panel. -->
+ <dimen name="internet_dialog_wifi_network_height">72dp</dimen>
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">@dimen/match_parent</dimen>
@@ -1276,10 +1280,21 @@
<dimen name="qs_tile_service_request_tile_width">192dp</dimen>
<dimen name="qs_tile_service_request_content_space">24dp</dimen>
- <dimen name="qs_dialog_button_horizontal_padding">16dp</dimen>
- <dimen name="qs_dialog_button_vertical_padding">8dp</dimen>
+ <!-- Dimensions for unified SystemUI dialogs styling. Used by Theme.SystemUI.Dialog and
+ alert_dialog_systemui.xml
+ -->
+ <dimen name="dialog_button_horizontal_padding">16dp</dimen>
+ <dimen name="dialog_button_vertical_padding">8dp</dimen>
<!-- The button will be 48dp tall, but the background needs to be 36dp tall -->
- <dimen name="qs_dialog_button_vertical_inset">6dp</dimen>
+ <dimen name="dialog_button_vertical_inset">6dp</dimen>
+ <dimen name="dialog_top_padding">24dp</dimen>
+ <dimen name="dialog_bottom_padding">18dp</dimen>
+ <dimen name="dialog_side_padding">24dp</dimen>
+ <dimen name="dialog_button_bar_top_padding">32dp</dimen>
+
+ <!-- ************************************************************************* -->
<dimen name="keyguard_unfold_translation_x">16dp</dimen>
+
+ <dimen name="fgs_manager_min_width_minor">100%</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 21b4a42..300cb2d3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2203,7 +2203,7 @@
<!-- Summary for disconnected status [CHAR LIMIT=50] -->
<string name="media_output_dialog_disconnected">(disconnected)</string>
<!-- Summary for connecting error message [CHAR LIMIT=NONE] -->
- <string name="media_output_dialog_connect_failed">Couldn\'t connect. Try again.</string>
+ <string name="media_output_dialog_connect_failed">Can\'t switch. Tap to try again.</string>
<!-- Title for pairing item [CHAR LIMIT=60] -->
<string name="media_output_dialog_pairing_new">Pair new device</string>
@@ -2358,4 +2358,9 @@
<!-- Title for User Switch dialog. [CHAR LIMIT=20] -->
<string name="qs_user_switch_dialog_title">Select user</string>
+
+ <!-- Title for dialog listing applications currently running in the backing [CHAR LIMIT=NONE]-->
+ <string name="fgs_manager_dialog_title">Apps running in the background</string>
+ <!-- Label of the button to stop the app from running in the background [CHAR LIMIT=12]-->
+ <string name="fgs_manager_app_item_stop_button_label">Stop</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d972b7fc..2c79919 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- NOTE: Adding the androidprv: namespace to this file will break the studio build. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<!-- HybridNotification themes and styles -->
@@ -351,11 +351,19 @@
<item name="android:windowIsFloating">true</item>
</style>
- <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
+ <style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog"/>
+
+ <style name="Theme.SystemUI.Dialog" parent="@style/Theme.SystemUI.DayNightDialog">
<item name="android:buttonCornerRadius">28dp</item>
- <item name="android:buttonBarPositiveButtonStyle">@style/Widget.QSDialog.Button</item>
- <item name="android:buttonBarNegativeButtonStyle">@style/Widget.QSDialog.Button.BorderButton</item>
- <item name="android:buttonBarNeutralButtonStyle">@style/Widget.QSDialog.Button.BorderButton</item>
+ <item name="android:buttonBarPositiveButtonStyle">@style/Widget.Dialog.Button</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
+ <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
+ <item name="android:colorBackground">?androidprv:attr/colorSurface</item>
+ <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
+ </style>
+
+ <style name="AlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault">
+ <item name="android:layout">@layout/alert_dialog_systemui</item>
</style>
<style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -365,11 +373,11 @@
<item name="android:windowIsFloating">true</item>
</style>
- <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
- <item name="android:windowIsFloating">true</item>
+ <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="Theme.SystemUI.Dialog">
+ <!-- Settings windowFullscreen: true is necessary to be able to intercept touch events -->
+ <!-- that would otherwise be intercepted by the Shade. -->
+ <item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:backgroundDimEnabled">true</item>
- <item name="android:windowCloseOnTouchOutside">true</item>
</style>
<style name="QSBorderlessButton">
@@ -446,6 +454,12 @@
<item name="android:background">@drawable/media_output_dialog_button_background</item>
</style>
+ <style name="MediaOutputRoundedButton" parent="@android:style/Widget.Material.Button">
+ <item name="android:background">@drawable/media_output_dialog_solid_button_background</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+
<style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:windowActionBar">false</item>
<item name="preferenceTheme">@style/TunerPreferenceTheme</item>
@@ -646,16 +660,6 @@
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
- <!-- TileService request dialog -->
- <style name="TileRequestDialog" parent="Theme.SystemUI.QuickSettings.Dialog">
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:windowBackground">@drawable/qs_dialog_bg</item>
- <item name="android:windowIsFloating">true</item>
- <item name="android:backgroundDimEnabled">true</item>
- <item name="android:windowCloseOnTouchOutside">true</item>
- <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
- </style>
-
<!-- USB Contaminant dialog -->
<style name ="USBContaminant" />
@@ -839,7 +843,6 @@
<style name="Widget.SliceView.Panel">
<item name="titleSize">16sp</item>
<item name="rowStyle">@style/SliceRow</item>
- <item name="android:background">?android:attr/colorBackgroundFloating</item>
</style>
<style name="SliceRow">
@@ -863,24 +866,37 @@
<item name="actionDividerHeight">32dp</item>
</style>
- <style name="TextAppearance.QSDialog.Title" parent="Theme.SystemUI.Dialog">
+ <style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">24sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:lineHeight">32sp</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textAlignment">center</item>
</style>
- <style name="Widget.QSDialog.Button" parent = "Theme.SystemUI.Dialog">
+ <style name="TextAppearance.Dialog.Body" parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:lineHeight">20sp</item>
+ </style>
+
+ <style name="TextAppearance.Dialog.Body.Message">
+ <item name="android:gravity">center</item>
+ <item name="android:textAlignment">center</item>
+ </style>
+
+ <style name="Widget.Dialog.Button" parent = "Theme.SystemUI.Dialog">
<item name="android:background">@drawable/qs_dialog_btn_filled</item>
- <item name="android:textColor">@color/prv_text_color_on_accent</item>
+ <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:stateListAnimator">@null</item>
- <item name="android:layout_marginHorizontal">4dp</item>
</style>
- <style name="Widget.QSDialog.Button.BorderButton">
+ <style name="Widget.Dialog.Button.BorderButton">
<item name="android:background">@drawable/qs_dialog_btn_outline</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
@@ -957,4 +973,20 @@
<style name="TextAppearance.InternetDialog.Secondary.Active"/>
+ <style name="InternetDialog.Divider">
+ <item name="android:background">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="InternetDialog.Divider.Active"/>
+
+ <style name="FgsManagerDialogTitle">
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textDirection">locale</item>
+ </style>
+
+ <style name="FgsManagerAppDuration">
+ <item name="android:fontFamily">?android:attr/textAppearanceSmall</item>
+ <item name="android:textDirection">locale</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index ed29bc7..5fdb497 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -298,4 +298,14 @@
<item>Off</item>
<item>On</item>
</string-array>
+
+ <!-- State names for fgsmanager tile: unavailable, off, on.
+ This subtitle is shown when the tile is in that particular state but does not set its own
+ subtitle, so some of these may never appear on screen. They should still be translated as
+ if they could appear.[CHAR LIMIT=32] -->
+ <string-array name="tile_states_fgsmanager">
+ <item>Unavailable</item>
+ <item>Off</item>
+ <item>On</item>
+ </string-array>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
index 07ad0c8..8aa3aba 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
@@ -51,6 +51,9 @@
private static final Interpolator ALPHA_OUT_INTERPOLATOR =
new PathInterpolator(0f, 0f, 0.8f, 1f);
+ @DimenRes
+ private final int mMaxWidthResource;
+
private Paint mRipplePaint;
private CanvasProperty<Float> mLeftProp;
private CanvasProperty<Float> mTopProp;
@@ -90,10 +93,17 @@
private Type mType = Type.ROUNDED_RECT;
public KeyButtonRipple(Context ctx, View targetView, @DimenRes int maxWidthResource) {
+ mMaxWidthResource = maxWidthResource;
mMaxWidth = ctx.getResources().getDimensionPixelSize(maxWidthResource);
mTargetView = targetView;
}
+ public void updateResources() {
+ mMaxWidth = mTargetView.getContext().getResources()
+ .getDimensionPixelSize(mMaxWidthResource);
+ invalidateSelf();
+ }
+
public void setDarkIntensity(float darkIntensity) {
mDark = darkIntensity >= 0.5f;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 8bd0f91..0149751 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -95,4 +95,9 @@
* Sent when screen turned on and ready to use (blocker scrim is hidden)
*/
void onScreenTurnedOn() = 21;
+
+ /**
+ * Sent when the desired dark intensity of the nav buttons has changed
+ */
+ void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 3128ffd..675dc9b5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -28,6 +28,7 @@
import android.os.Parcelable;
import android.view.ViewDebug;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.PrintWriter;
@@ -50,6 +51,7 @@
@ViewDebug.ExportedProperty(category="recents")
public int windowingMode;
@ViewDebug.ExportedProperty(category="recents")
+ @NonNull
public final Intent baseIntent;
@ViewDebug.ExportedProperty(category="recents")
public final int userId;
@@ -83,7 +85,7 @@
updateHashCode();
}
- public TaskKey(int id, int windowingMode, Intent intent,
+ public TaskKey(int id, int windowingMode, @NonNull Intent intent,
ComponentName sourceComponent, int userId, long lastActiveTime) {
this.id = id;
this.windowingMode = windowingMode;
@@ -95,7 +97,7 @@
updateHashCode();
}
- public TaskKey(int id, int windowingMode, Intent intent,
+ public TaskKey(int id, int windowingMode, @NonNull Intent intent,
ComponentName sourceComponent, int userId, long lastActiveTime, int displayId) {
this.id = id;
this.windowingMode = windowingMode;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 6154d84..8d98a75 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -18,7 +18,6 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import android.annotation.TargetApi;
import android.content.Context;
@@ -126,10 +125,9 @@
final WindowManager windowManager = context.getSystemService(WindowManager.class);
final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
- float originalSmallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
+ float smallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
context.getResources().getConfiguration().densityDpi);
- return dpiFromPx(Math.min(bounds.width(), bounds.height()), DENSITY_DEVICE_STABLE)
- >= TABLET_MIN_DPS && originalSmallestWidth >= TABLET_MIN_DPS;
+ return smallestWidth >= TABLET_MIN_DPS;
}
public static float dpiFromPx(float size, int densityDpi) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index cbf7397..857cc462 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -21,6 +21,8 @@
import android.annotation.LayoutRes;
import android.annotation.StringRes;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -29,12 +31,12 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import androidx.core.view.OneShotPreDrawListener;
-import com.android.systemui.shared.R;
import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
/**
@@ -48,7 +50,21 @@
private final ViewGroup mKeyButtonContainer;
private final FloatingRotationButtonView mKeyButtonView;
- private final int mContainerSize;
+ private int mContainerSize;
+ private final Context mContext;
+
+ @StringRes
+ private final int mContentDescriptionResource;
+ @DimenRes
+ private final int mMinMarginResource;
+ @DimenRes
+ private final int mRoundedContentPaddingResource;
+ @DimenRes
+ private final int mTaskbarLeftMarginResource;
+ @DimenRes
+ private final int mTaskbarBottomMarginResource;
+ @DimenRes
+ private final int mButtonDiameterResource;
private AnimatedVectorDrawable mAnimatedDrawable;
private boolean mIsShowing;
@@ -58,13 +74,13 @@
private boolean mIsTaskbarVisible = false;
private boolean mIsTaskbarStashed = false;
- private final FloatingRotationButtonPositionCalculator mPositionCalculator;
+ private FloatingRotationButtonPositionCalculator mPositionCalculator;
private RotationButtonController mRotationButtonController;
private RotationButtonUpdatesCallback mUpdatesCallback;
private Position mPosition;
- public FloatingRotationButton(Context context, @StringRes int contentDescription,
+ public FloatingRotationButton(Context context, @StringRes int contentDescriptionResource,
@LayoutRes int layout, @IdRes int keyButtonId, @DimenRes int minMargin,
@DimenRes int roundedContentPadding, @DimenRes int taskbarLeftMargin,
@DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
@@ -73,24 +89,37 @@
mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(layout, null);
mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
mKeyButtonView.setVisibility(View.VISIBLE);
- mKeyButtonView.setContentDescription(context.getString(contentDescription));
+ mKeyButtonView.setContentDescription(context.getString(contentDescriptionResource));
mKeyButtonView.setRipple(rippleMaxWidth);
- Resources res = context.getResources();
+ mContext = context;
+
+ mContentDescriptionResource = contentDescriptionResource;
+ mMinMarginResource = minMargin;
+ mRoundedContentPaddingResource = roundedContentPadding;
+ mTaskbarLeftMarginResource = taskbarLeftMargin;
+ mTaskbarBottomMarginResource = taskbarBottomMargin;
+ mButtonDiameterResource = buttonDiameter;
+
+ updateDimensionResources();
+ }
+
+ private void updateDimensionResources() {
+ Resources res = mContext.getResources();
int defaultMargin = Math.max(
- res.getDimensionPixelSize(minMargin),
- res.getDimensionPixelSize(roundedContentPadding));
+ res.getDimensionPixelSize(mMinMarginResource),
+ res.getDimensionPixelSize(mRoundedContentPaddingResource));
int taskbarMarginLeft =
- res.getDimensionPixelSize(taskbarLeftMargin);
+ res.getDimensionPixelSize(mTaskbarLeftMarginResource);
int taskbarMarginBottom =
- res.getDimensionPixelSize(taskbarBottomMargin);
+ res.getDimensionPixelSize(mTaskbarBottomMarginResource);
mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
taskbarMarginLeft, taskbarMarginBottom);
- final int diameter = res.getDimensionPixelSize(buttonDiameter);
+ final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
taskbarMarginBottom));
}
@@ -119,32 +148,10 @@
}
mIsShowing = true;
- int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- // TODO(b/200103245): add new window type that has z-index above
- // TYPE_NAVIGATION_BAR_PANEL as currently it could be below the taskbar which has
- // the same window type
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- mContainerSize,
- mContainerSize,
- 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
- PixelFormat.TRANSLUCENT);
+ final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
+ mWindowManager.addView(mKeyButtonContainer, layoutParams);
- lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- lp.setTitle("FloatingRotationButton");
- lp.setFitInsetsTypes(0 /*types */);
-
- mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
- mPosition = mPositionCalculator
- .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
-
- lp.gravity = mPosition.getGravity();
- ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
- mPosition.getGravity();
-
- updateTranslation(mPosition, /* animate */ false);
-
- mWindowManager.addView(mKeyButtonContainer, lp);
if (mAnimatedDrawable != null) {
mAnimatedDrawable.reset();
mAnimatedDrawable.start();
@@ -232,6 +239,53 @@
}
}
+ /**
+ * Updates resources that could be changed in runtime, should be called on configuration
+ * change with changes diff integer mask
+ * @param configurationChanges - configuration changes with flags from ActivityInfo e.g.
+ * {@link android.content.pm.ActivityInfo#CONFIG_DENSITY}
+ */
+ public void onConfigurationChanged(@Config int configurationChanges) {
+ if ((configurationChanges & ActivityInfo.CONFIG_DENSITY) != 0
+ || (configurationChanges & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+ updateDimensionResources();
+
+ if (mIsShowing) {
+ final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
+ mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
+ }
+ }
+
+ if ((configurationChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+ mKeyButtonView.setContentDescription(mContext.getString(mContentDescriptionResource));
+ }
+ }
+
+ private LayoutParams adjustViewPositionAndCreateLayoutParams() {
+ final LayoutParams lp = new LayoutParams(
+ mContainerSize,
+ mContainerSize,
+ /* xpos */ 0, /* ypos */ 0, LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+
+ lp.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setTitle("FloatingRotationButton");
+ lp.setFitInsetsTypes(/* types */ 0);
+
+ mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
+ mPosition = mPositionCalculator
+ .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+ lp.gravity = mPosition.getGravity();
+ ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
+ mPosition.getGravity();
+
+ updateTranslation(mPosition, /* animate */ false);
+
+ return lp;
+ }
+
private void updateTranslation(Position position, boolean animate) {
final int translationX = position.getTranslationX();
final int translationY = position.getTranslationY();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index c5f8fc1..a4b6451 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -17,6 +17,8 @@
package com.android.systemui.shared.rotation;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -37,12 +39,15 @@
private KeyButtonRipple mRipple;
private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ private final Configuration mLastConfiguration;
+
public FloatingRotationButtonView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingRotationButtonView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mLastConfiguration = getResources().getConfiguration();
setClickable(true);
@@ -63,6 +68,17 @@
}
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ final int changes = mLastConfiguration.updateFrom(newConfig);
+ if ((changes & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
+ || ((changes & ActivityInfo.CONFIG_DENSITY) != 0)) {
+ if (mRipple != null) {
+ mRipple.updateResources();
+ }
+ }
+ }
+
public void setColors(int lightColor, int darkColor) {
getDrawable().setColorFilter(new PorterDuffColorFilter(lightColor, PorterDuff.Mode.SRC_IN));
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 2dbd5de..78867f7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -481,7 +481,9 @@
* orientation overview.
*/
public void setSkipOverrideUserLockPrefsOnce() {
- mSkipOverrideUserLockPrefsOnce = true;
+ // If live-tile is enabled (recents animation keeps running in overview), there is no
+ // activity switch so the display rotation is not changed, then it is no need to skip.
+ mSkipOverrideUserLockPrefsOnce = !mIsRecentsAnimationRunning;
}
private boolean shouldOverrideUserLockPrefs(final int rotation) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 3ebd652..986f296 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -479,9 +479,7 @@
Resources resources = mView.getResources();
- if (resources.getBoolean(R.bool.can_use_one_handed_bouncer)
- && resources.getBoolean(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
+ if (resources.getBoolean(R.bool.can_use_one_handed_bouncer)) {
gravity = resources.getInteger(
R.integer.keyguard_host_view_one_handed_gravity);
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index abd89b9..172c7f6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -30,6 +30,7 @@
import android.graphics.Rect;
import android.provider.Settings;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.Gravity;
@@ -37,6 +38,7 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
@@ -44,6 +46,8 @@
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -58,6 +62,7 @@
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.util.settings.GlobalSettings;
import java.util.ArrayList;
import java.util.List;
@@ -67,6 +72,12 @@
static final int USER_TYPE_WORK_PROFILE = 2;
static final int USER_TYPE_SECONDARY_USER = 3;
+ @IntDef({MODE_DEFAULT, MODE_ONE_HANDED, MODE_USER_SWITCHER})
+ public @interface Mode {}
+ static final int MODE_DEFAULT = 0;
+ static final int MODE_ONE_HANDED = 1;
+ static final int MODE_USER_SWITCHER = 2;
+
// Bouncer is dismissed due to no security.
static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
// Bouncer is dismissed due to pin, password or pattern entered.
@@ -78,6 +89,8 @@
// Bouncer is dismissed due to sim card unlock code entered.
static final int BOUNCER_DISMISS_SIM = 4;
+ private static final String TAG = "KeyguardSecurityView";
+
// Make the view move slower than the finger, as if the spring were applying force.
private static final float TOUCH_Y_MULTIPLIER = 0.25f;
// How much you need to drag the bouncer to trigger an auth retry (in dps.)
@@ -96,6 +109,7 @@
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
+ private GlobalSettings mGlobalSettings;
private AlertDialog mAlertDialog;
private boolean mSwipeUpToRetry;
@@ -110,10 +124,8 @@
private float mStartTouchY = -1;
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
-
- private boolean mIsSecurityViewLeftAligned = true;
- private boolean mOneHandedMode = false;
- @Nullable private ValueAnimator mRunningOneHandedAnimator;
+ private ModeLogic mModeLogic = new DefaultModeLogic();
+ private @Mode int mCurrentMode = MODE_DEFAULT;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -260,172 +272,62 @@
updateBiometricRetry(securityMode, faceAuthEnabled);
}
- /**
- * Sets whether this security container is in one handed mode. If so, it will measure its
- * child SecurityViewFlipper in one half of the screen, and move it when tapping on the opposite
- * side of the screen.
- */
- public void setOneHandedMode(boolean oneHandedMode) {
- mOneHandedMode = oneHandedMode;
- updateSecurityViewGravity();
- updateSecurityViewLocation(false);
+ void initMode(@Mode int mode, GlobalSettings globalSettings) {
+ if (mCurrentMode == mode) return;
+ Log.i(TAG, "Switching mode from " + modeToString(mCurrentMode) + " to "
+ + modeToString(mode));
+ mCurrentMode = mode;
+
+ switch (mode) {
+ case MODE_ONE_HANDED:
+ mModeLogic = new OneHandedModeLogic();
+ break;
+ case MODE_USER_SWITCHER:
+ mModeLogic = new UserSwitcherModeLogic();
+ break;
+ default:
+ mModeLogic = new DefaultModeLogic();
+ }
+ mGlobalSettings = globalSettings;
+ finishSetup();
}
- /** Returns whether this security container is in one-handed mode. */
- public boolean isOneHandedMode() {
- return mOneHandedMode;
+ private String modeToString(@Mode int mode) {
+ switch (mode) {
+ case MODE_DEFAULT:
+ return "Default";
+ case MODE_ONE_HANDED:
+ return "OneHanded";
+ case MODE_USER_SWITCHER:
+ return "UserSwitcher";
+ default:
+ throw new IllegalArgumentException("mode: " + mode + " not supported");
+ }
+ }
+
+ private void finishSetup() {
+ if (mSecurityViewFlipper == null || mGlobalSettings == null) return;
+
+ mModeLogic.init(this, mGlobalSettings, mSecurityViewFlipper);
+ }
+
+ @Mode int getMode() {
+ return mCurrentMode;
}
/**
- * When in one-handed mode, sets if the inner SecurityViewFlipper should be aligned to the
- * left-hand side of the screen or not, and whether to animate when moving between the two.
+ * The position of the container can be adjusted based upon a touch at location x. This has
+ * been used in one-handed mode to make sure the bouncer appears on the side of the display
+ * that the user last interacted with.
*/
- public void setOneHandedModeLeftAligned(boolean leftAligned, boolean animate) {
- mIsSecurityViewLeftAligned = leftAligned;
- updateSecurityViewLocation(animate);
+ void updatePositionByTouchX(float x) {
+ mModeLogic.updatePositionByTouchX(x);
}
/** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
public boolean isOneHandedModeLeftAligned() {
- return mIsSecurityViewLeftAligned;
- }
-
- private void updateSecurityViewGravity() {
- if (mSecurityViewFlipper == null) {
- return;
- }
-
- FrameLayout.LayoutParams lp =
- (FrameLayout.LayoutParams) mSecurityViewFlipper.getLayoutParams();
-
- if (mOneHandedMode) {
- lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
- } else {
- lp.gravity = Gravity.CENTER_HORIZONTAL;
- }
-
- mSecurityViewFlipper.setLayoutParams(lp);
- }
-
- /**
- * Moves the inner security view to the correct location (in one handed mode) with animation.
- * This is triggered when the user taps on the side of the screen that is not currently occupied
- * by the security view .
- */
- private void updateSecurityViewLocation(boolean animate) {
- if (mSecurityViewFlipper == null) {
- return;
- }
-
- if (!mOneHandedMode) {
- mSecurityViewFlipper.setTranslationX(0);
- return;
- }
-
- if (mRunningOneHandedAnimator != null) {
- mRunningOneHandedAnimator.cancel();
- mRunningOneHandedAnimator = null;
- }
-
- int targetTranslation = mIsSecurityViewLeftAligned
- ? 0 : (int) (getMeasuredWidth() - mSecurityViewFlipper.getWidth());
-
- if (animate) {
- // This animation is a bit fun to implement. The bouncer needs to move, and fade in/out
- // at the same time. The issue is, the bouncer should only move a short amount (120dp or
- // so), but obviously needs to go from one side of the screen to the other. This needs a
- // pretty custom animation.
- //
- // This works as follows. It uses a ValueAnimation to simply drive the animation
- // progress. This animator is responsible for both the translation of the bouncer, and
- // the current fade. It will fade the bouncer out while also moving it along the 120dp
- // path. Once the bouncer is fully faded out though, it will "snap" the bouncer closer
- // to its destination, then fade it back in again. The effect is that the bouncer will
- // move from 0 -> X while fading out, then (destination - X) -> destination while fading
- // back in again.
- // TODO(b/195012405): Make this animation properly abortable.
- Interpolator positionInterpolator = AnimationUtils.loadInterpolator(
- mContext, android.R.interpolator.fast_out_extra_slow_in);
- Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
- Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
-
- mRunningOneHandedAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- mRunningOneHandedAnimator.setDuration(BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS);
- mRunningOneHandedAnimator.setInterpolator(Interpolators.LINEAR);
-
- int initialTranslation = (int) mSecurityViewFlipper.getTranslationX();
- int totalTranslation = (int) getResources().getDimension(
- R.dimen.one_handed_bouncer_move_animation_translation);
-
- final boolean shouldRestoreLayerType = mSecurityViewFlipper.hasOverlappingRendering()
- && mSecurityViewFlipper.getLayerType() != View.LAYER_TYPE_HARDWARE;
- if (shouldRestoreLayerType) {
- mSecurityViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
- }
-
- float initialAlpha = mSecurityViewFlipper.getAlpha();
-
- mRunningOneHandedAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRunningOneHandedAnimator = null;
- }
- });
- mRunningOneHandedAnimator.addUpdateListener(animation -> {
- float switchPoint = BOUNCER_HANDEDNESS_ANIMATION_FADE_OUT_PROPORTION;
- boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
-
- int currentTranslation = (int) (positionInterpolator.getInterpolation(
- animation.getAnimatedFraction()) * totalTranslation);
- int translationRemaining = totalTranslation - currentTranslation;
-
- // Flip the sign if we're going from right to left.
- if (mIsSecurityViewLeftAligned) {
- currentTranslation = -currentTranslation;
- translationRemaining = -translationRemaining;
- }
-
- if (isFadingOut) {
- // The bouncer fades out over the first X%.
- float fadeOutFraction = MathUtils.constrainedMap(
- /* rangeMin= */1.0f,
- /* rangeMax= */0.0f,
- /* valueMin= */0.0f,
- /* valueMax= */switchPoint,
- animation.getAnimatedFraction());
- float opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
-
- // When fading out, the alpha needs to start from the initial opacity of the
- // view flipper, otherwise we get a weird bit of jank as it ramps back to 100%.
- mSecurityViewFlipper.setAlpha(opacity * initialAlpha);
-
- // Animate away from the source.
- mSecurityViewFlipper.setTranslationX(initialTranslation + currentTranslation);
- } else {
- // And in again over the remaining (100-X)%.
- float fadeInFraction = MathUtils.constrainedMap(
- /* rangeMin= */0.0f,
- /* rangeMax= */1.0f,
- /* valueMin= */switchPoint,
- /* valueMax= */1.0f,
- animation.getAnimatedFraction());
-
- float opacity = fadeInInterpolator.getInterpolation(fadeInFraction);
- mSecurityViewFlipper.setAlpha(opacity);
-
- // Fading back in, animate towards the destination.
- mSecurityViewFlipper.setTranslationX(targetTranslation - translationRemaining);
- }
-
- if (animation.getAnimatedFraction() == 1.0f && shouldRestoreLayerType) {
- mSecurityViewFlipper.setLayerType(View.LAYER_TYPE_NONE, /* paint= */null);
- }
- });
-
- mRunningOneHandedAnimator.start();
- } else {
- mSecurityViewFlipper.setTranslationX(targetTranslation);
- }
+ return mCurrentMode == MODE_ONE_HANDED
+ && ((OneHandedModeLogic) mModeLogic).isLeftAligned();
}
public void onPause() {
@@ -526,7 +428,7 @@
}
} else {
if (!mIsDragging) {
- handleTap(event);
+ mModeLogic.handleTap(event);
}
}
}
@@ -541,36 +443,6 @@
mMotionEventListeners.remove(listener);
}
- private void handleTap(MotionEvent event) {
- // If we're using a fullscreen security mode, skip
- if (!mOneHandedMode) {
- return;
- }
-
- moveBouncerForXCoordinate(event.getX(), /* animate= */true);
- }
-
- private void moveBouncerForXCoordinate(float x, boolean animate) {
- // Did the tap hit the "other" side of the bouncer?
- if ((mIsSecurityViewLeftAligned && (x > getWidth() / 2f))
- || (!mIsSecurityViewLeftAligned && (x < getWidth() / 2f))) {
- mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
-
- Settings.Global.putInt(
- mContext.getContentResolver(),
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
- mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
- : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
-
- int keyguardState = mIsSecurityViewLeftAligned
- ? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_LEFT
- : SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_RIGHT;
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, keyguardState);
-
- updateSecurityViewLocation(animate);
- }
- }
-
void setSwipeListener(SwipeListener swipeListener) {
mSwipeListener = swipeListener;
}
@@ -618,6 +490,8 @@
public void onFinishInflate() {
super.onFinishInflate();
mSecurityViewFlipper = findViewById(R.id.view_flipper);
+
+ finishSetup();
}
@Override
@@ -685,20 +559,15 @@
int maxWidth = 0;
int childState = 0;
- int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(widthMeasureSpec) / 2,
- MeasureSpec.getMode(widthMeasureSpec));
-
for (int i = 0; i < getChildCount(); i++) {
final View view = getChildAt(i);
if (view.getVisibility() != GONE) {
- if (mOneHandedMode && view == mSecurityViewFlipper) {
- measureChildWithMargins(view, halfWidthMeasureSpec, 0,
- heightMeasureSpec, 0);
- } else {
- measureChildWithMargins(view, widthMeasureSpec, 0,
- heightMeasureSpec, 0);
+ int updatedWidthMeasureSpec = widthMeasureSpec;
+ if (view == mSecurityViewFlipper) {
+ updatedWidthMeasureSpec = mModeLogic.getChildWidthMeasureSpec(widthMeasureSpec);
}
+ measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0);
+
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
maxWidth = Math.max(maxWidth,
view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
@@ -726,7 +595,7 @@
// After a layout pass, we need to re-place the inner bouncer, as our bounds may have
// changed.
- updateSecurityViewLocation(/* animate= */false);
+ mModeLogic.updateSecurityViewLocation();
}
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
@@ -770,4 +639,264 @@
public void reset() {
mDisappearAnimRunning = false;
}
+
+ /**
+ * Enscapsulates the differences between bouncer modes for the container.
+ */
+ private interface ModeLogic {
+
+ default void init(ViewGroup v, GlobalSettings globalSettings,
+ KeyguardSecurityViewFlipper viewFlipper) {};
+
+ /** Reinitialize the location */
+ default void updateSecurityViewLocation() {};
+
+ /** Alter the ViewFlipper position, based upon a touch outside of it */
+ default void updatePositionByTouchX(float x) {};
+
+ /** A tap on the container, outside of the ViewFlipper */
+ default void handleTap(MotionEvent event) {};
+
+ /** Override to alter the width measure spec to perhaps limit the ViewFlipper size */
+ default int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
+ return parentWidthMeasureSpec;
+ }
+ }
+
+ private static class DefaultModeLogic implements ModeLogic {
+ private ViewGroup mView;
+ private KeyguardSecurityViewFlipper mViewFlipper;
+
+ @Override
+ public void init(ViewGroup v, GlobalSettings globalSettings,
+ KeyguardSecurityViewFlipper viewFlipper) {
+ mView = v;
+ mViewFlipper = viewFlipper;
+
+ // Reset ViewGroup to default positions
+ updateSecurityViewGroup();
+ }
+
+ private void updateSecurityViewGroup() {
+ FrameLayout.LayoutParams lp =
+ (FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
+ lp.gravity = Gravity.CENTER_HORIZONTAL;
+ mViewFlipper.setLayoutParams(lp);
+
+ mViewFlipper.setTranslationX(0);
+ }
+ }
+
+ /**
+ * User switcher mode will display both the current user icon as well as
+ * a user switcher, in both portrait and landscape modes.
+ */
+ private static class UserSwitcherModeLogic implements ModeLogic {
+ private ViewGroup mView;
+
+ @Override
+ public void init(ViewGroup v, GlobalSettings globalSettings,
+ KeyguardSecurityViewFlipper viewFlipper) {
+ mView = v;
+ }
+ }
+
+ /**
+ * Logic to enabled one-handed bouncer mode. Supports animating the bouncer
+ * between alternate sides of the display.
+ */
+ private static class OneHandedModeLogic implements ModeLogic {
+ @Nullable private ValueAnimator mRunningOneHandedAnimator;
+ private ViewGroup mView;
+ private KeyguardSecurityViewFlipper mViewFlipper;
+ private GlobalSettings mGlobalSettings;
+
+ @Override
+ public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ @NonNull KeyguardSecurityViewFlipper viewFlipper) {
+ mView = v;
+ mViewFlipper = viewFlipper;
+ mGlobalSettings = globalSettings;
+
+ updateSecurityViewGravity();
+ updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
+ }
+
+ /**
+ * One-handed mode contains the child to half of the available space.
+ */
+ @Override
+ public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
+ return MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
+ MeasureSpec.getMode(parentWidthMeasureSpec));
+ }
+
+ private void updateSecurityViewGravity() {
+ FrameLayout.LayoutParams lp =
+ (FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
+ lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ mViewFlipper.setLayoutParams(lp);
+ }
+
+ /**
+ * Moves the bouncer to align with a tap (most likely in the shade), so the bouncer
+ * appears on the same side as a touch. Will not update the user-preference.
+ */
+ @Override
+ public void updatePositionByTouchX(float x) {
+ updateSecurityViewLocation(x <= mView.getWidth() / 2f, /* animate= */false);
+ }
+
+ boolean isLeftAligned() {
+ return mGlobalSettings.getInt(Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ }
+
+ /**
+ * Determine if a tap on this view is on the other side. If so, will animate positions
+ * and record the preference to always show on this side.
+ */
+ @Override
+ public void handleTap(MotionEvent event) {
+ float x = event.getX();
+ boolean currentlyLeftAligned = isLeftAligned();
+ // Did the tap hit the "other" side of the bouncer?
+ if ((currentlyLeftAligned && (x > mView.getWidth() / 2f))
+ || (!currentlyLeftAligned && (x < mView.getWidth() / 2f))) {
+
+ boolean willBeLeftAligned = !currentlyLeftAligned;
+ mGlobalSettings.putInt(
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
+ willBeLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
+ : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+
+ int keyguardState = willBeLeftAligned
+ ? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_LEFT
+ : SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_RIGHT;
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, keyguardState);
+
+ updateSecurityViewLocation(willBeLeftAligned, true /* animate */);
+ }
+ }
+
+ @Override
+ public void updateSecurityViewLocation() {
+ updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
+ }
+
+ /**
+ * Moves the inner security view to the correct location (in one handed mode) with
+ * animation. This is triggered when the user taps on the side of the screen that is not
+ * currently occupied by the security view.
+ */
+ private void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
+ if (mRunningOneHandedAnimator != null) {
+ mRunningOneHandedAnimator.cancel();
+ mRunningOneHandedAnimator = null;
+ }
+
+ int targetTranslation = leftAlign
+ ? 0 : (int) (mView.getMeasuredWidth() - mViewFlipper.getWidth());
+
+ if (animate) {
+ // This animation is a bit fun to implement. The bouncer needs to move, and fade
+ // in/out at the same time. The issue is, the bouncer should only move a short
+ // amount (120dp or so), but obviously needs to go from one side of the screen to
+ // the other. This needs a pretty custom animation.
+ //
+ // This works as follows. It uses a ValueAnimation to simply drive the animation
+ // progress. This animator is responsible for both the translation of the bouncer,
+ // and the current fade. It will fade the bouncer out while also moving it along the
+ // 120dp path. Once the bouncer is fully faded out though, it will "snap" the
+ // bouncer closer to its destination, then fade it back in again. The effect is that
+ // the bouncer will move from 0 -> X while fading out, then
+ // (destination - X) -> destination while fading back in again.
+ // TODO(b/208250221): Make this animation properly abortable.
+ Interpolator positionInterpolator = AnimationUtils.loadInterpolator(
+ mView.getContext(), android.R.interpolator.fast_out_extra_slow_in);
+ Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
+ Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
+
+ mRunningOneHandedAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mRunningOneHandedAnimator.setDuration(BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS);
+ mRunningOneHandedAnimator.setInterpolator(Interpolators.LINEAR);
+
+ int initialTranslation = (int) mViewFlipper.getTranslationX();
+ int totalTranslation = (int) mView.getResources().getDimension(
+ R.dimen.one_handed_bouncer_move_animation_translation);
+
+ final boolean shouldRestoreLayerType = mViewFlipper.hasOverlappingRendering()
+ && mViewFlipper.getLayerType() != View.LAYER_TYPE_HARDWARE;
+ if (shouldRestoreLayerType) {
+ mViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
+ }
+
+ float initialAlpha = mViewFlipper.getAlpha();
+
+ mRunningOneHandedAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningOneHandedAnimator = null;
+ }
+ });
+ mRunningOneHandedAnimator.addUpdateListener(animation -> {
+ float switchPoint = BOUNCER_HANDEDNESS_ANIMATION_FADE_OUT_PROPORTION;
+ boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
+
+ int currentTranslation = (int) (positionInterpolator.getInterpolation(
+ animation.getAnimatedFraction()) * totalTranslation);
+ int translationRemaining = totalTranslation - currentTranslation;
+
+ // Flip the sign if we're going from right to left.
+ if (leftAlign) {
+ currentTranslation = -currentTranslation;
+ translationRemaining = -translationRemaining;
+ }
+
+ if (isFadingOut) {
+ // The bouncer fades out over the first X%.
+ float fadeOutFraction = MathUtils.constrainedMap(
+ /* rangeMin= */1.0f,
+ /* rangeMax= */0.0f,
+ /* valueMin= */0.0f,
+ /* valueMax= */switchPoint,
+ animation.getAnimatedFraction());
+ float opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
+
+ // When fading out, the alpha needs to start from the initial opacity of the
+ // view flipper, otherwise we get a weird bit of jank as it ramps back to
+ // 100%.
+ mViewFlipper.setAlpha(opacity * initialAlpha);
+
+ // Animate away from the source.
+ mViewFlipper.setTranslationX(initialTranslation + currentTranslation);
+ } else {
+ // And in again over the remaining (100-X)%.
+ float fadeInFraction = MathUtils.constrainedMap(
+ /* rangeMin= */0.0f,
+ /* rangeMax= */1.0f,
+ /* valueMin= */switchPoint,
+ /* valueMax= */1.0f,
+ animation.getAnimatedFraction());
+
+ float opacity = fadeInInterpolator.getInterpolation(fadeInFraction);
+ mViewFlipper.setAlpha(opacity);
+
+ // Fading back in, animate towards the destination.
+ mViewFlipper.setTranslationX(targetTranslation - translationRemaining);
+ }
+
+ if (animation.getAnimatedFraction() == 1.0f && shouldRestoreLayerType) {
+ mViewFlipper.setLayerType(View.LAYER_TYPE_NONE, /* paint= */null);
+ }
+ });
+
+ mRunningOneHandedAnimator.start();
+ } else {
+ mViewFlipper.setTranslationX(targetTranslation);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index d4d3d5b..4035229 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -32,7 +32,6 @@
import android.content.res.Configuration;
import android.metrics.LogMaker;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
@@ -56,6 +55,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
+import com.android.systemui.util.settings.GlobalSettings;
import javax.inject.Inject;
@@ -78,6 +78,7 @@
private final SecurityCallback mSecurityCallback;
private final ConfigurationController mConfigurationController;
private final FalsingCollector mFalsingCollector;
+ private final GlobalSettings mGlobalSettings;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -99,10 +100,10 @@
// If we're in one handed mode, the user can tap on the opposite side of the screen
// to move the bouncer across. In that case, inhibit the falsing (otherwise the taps
// to move the bouncer to each screen side can end up closing it instead).
- if (mView.isOneHandedMode()) {
- if ((mView.isOneHandedModeLeftAligned() && ev.getX() > mView.getWidth() / 2f)
- || (!mView.isOneHandedModeLeftAligned()
- && ev.getX() <= mView.getWidth() / 2f)) {
+ if (mView.getMode() == KeyguardSecurityContainer.MODE_ONE_HANDED) {
+ boolean isLeftAligned = mView.isOneHandedModeLeftAligned();
+ if ((isLeftAligned && ev.getX() > mView.getWidth() / 2f)
+ || (!isLeftAligned && ev.getX() <= mView.getWidth() / 2f)) {
mFalsingCollector.avoidGesture();
}
}
@@ -152,8 +153,8 @@
public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
int bouncerSide = SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__DEFAULT;
- if (canUseOneHandedBouncer()) {
- bouncerSide = isOneHandedKeyguardLeftAligned()
+ if (mView.getMode() == KeyguardSecurityContainer.MODE_ONE_HANDED) {
+ bouncerSide = mView.isOneHandedModeLeftAligned()
? SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__LEFT
: SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__RIGHT;
}
@@ -230,7 +231,8 @@
SecurityCallback securityCallback,
KeyguardSecurityViewFlipperController securityViewFlipperController,
ConfigurationController configurationController,
- FalsingCollector falsingCollector) {
+ FalsingCollector falsingCollector,
+ GlobalSettings globalSettings) {
super(view);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -245,6 +247,7 @@
mConfigurationController = configurationController;
mLastOrientation = getResources().getConfiguration().orientation;
mFalsingCollector = falsingCollector;
+ mGlobalSettings = globalSettings;
}
@Override
@@ -324,7 +327,7 @@
public void onResume(int reason) {
if (mCurrentSecurityMode != SecurityMode.None) {
int state = SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN;
- if (canUseOneHandedBouncer()) {
+ if (mView.getMode() == KeyguardSecurityContainer.MODE_ONE_HANDED) {
state = mView.isOneHandedModeLeftAligned()
? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN_LEFT
: SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN_RIGHT;
@@ -477,47 +480,41 @@
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
- configureOneHandedMode();
+ configureMode();
}
mSecurityCallback.onSecurityModeChanged(
securityMode, newView != null && newView.needsInput());
}
- /** Read whether the one-handed keyguard should be on the left/right from settings. */
- private boolean isOneHandedKeyguardLeftAligned() {
- try {
- return Settings.Global.getInt(mView.getContext().getContentResolver(),
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
- == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
- } catch (Settings.SettingNotFoundException ex) {
- return true;
- }
- }
-
+ /**
+ * Returns whether the given security view should be used in a "one handed" way. This can be
+ * used to change how the security view is drawn (e.g. take up less of the screen, and align to
+ * one side).
+ */
private boolean canUseOneHandedBouncer() {
- // Is it enabled?
- if (!getResources().getBoolean(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
- return false;
- }
-
- if (!KeyguardSecurityModel.isSecurityViewOneHanded(mCurrentSecurityMode)) {
+ if (!(mCurrentSecurityMode == SecurityMode.Pattern
+ || mCurrentSecurityMode == SecurityMode.PIN)) {
return false;
}
return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
}
- private void configureOneHandedMode() {
- boolean oneHandedMode = canUseOneHandedBouncer();
+ private boolean canDisplayUserSwitcher() {
+ return getResources().getBoolean(R.bool.bouncer_display_user_switcher);
+ }
- mView.setOneHandedMode(oneHandedMode);
-
- if (oneHandedMode) {
- mView.setOneHandedModeLeftAligned(
- isOneHandedKeyguardLeftAligned(), /* animate= */false);
+ private void configureMode() {
+ // One-handed mode and user-switcher are currently mutually exclusive, and enforced here
+ int mode = KeyguardSecurityContainer.MODE_DEFAULT;
+ if (canDisplayUserSwitcher()) {
+ mode = KeyguardSecurityContainer.MODE_USER_SWITCHER;
+ } else if (canUseOneHandedBouncer()) {
+ mode = KeyguardSecurityContainer.MODE_ONE_HANDED;
}
+
+ mView.initMode(mode, mGlobalSettings);
}
public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
@@ -584,15 +581,13 @@
int newOrientation = getResources().getConfiguration().orientation;
if (newOrientation != mLastOrientation) {
mLastOrientation = newOrientation;
- configureOneHandedMode();
+ configureMode();
}
}
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
- if (mView.isOneHandedMode()) {
- mView.setOneHandedModeLeftAligned(x <= mView.getWidth() / 2f, false);
- }
+ mView.updatePositionByTouchX(x);
}
static class Factory {
@@ -609,6 +604,7 @@
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final ConfigurationController mConfigurationController;
private final FalsingCollector mFalsingCollector;
+ private final GlobalSettings mGlobalSettings;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -622,7 +618,8 @@
KeyguardStateController keyguardStateController,
KeyguardSecurityViewFlipperController securityViewFlipperController,
ConfigurationController configurationController,
- FalsingCollector falsingCollector) {
+ FalsingCollector falsingCollector,
+ GlobalSettings globalSettings) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
mLockPatternUtils = lockPatternUtils;
@@ -634,6 +631,7 @@
mSecurityViewFlipperController = securityViewFlipperController;
mConfigurationController = configurationController;
mFalsingCollector = falsingCollector;
+ mGlobalSettings = globalSettings;
}
public KeyguardSecurityContainerController create(
@@ -642,7 +640,7 @@
mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
- mConfigurationController, mFalsingCollector);
+ mConfigurationController, mFalsingCollector, mGlobalSettings);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index 69328cd..bacd29f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -94,13 +94,4 @@
throw new IllegalStateException("Unknown security quality:" + security);
}
}
-
- /**
- * Returns whether the given security view should be used in a "one handed" way. This can be
- * used to change how the security view is drawn (e.g. take up less of the screen, and align to
- * one side).
- */
- public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
- return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e24f07c..ba67716 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -144,7 +144,7 @@
private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_FINGERPRINT = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_SPEW = false;
- private static final int FINGERPRINT_LOCKOUT_RESET_DELAY_MS = 600;
+ private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
private static final String ACTION_FACE_UNLOCK_STARTED
= "com.android.facelock.FACE_UNLOCK_STARTED";
@@ -201,6 +201,19 @@
private static final int BIOMETRIC_STATE_CANCELLING = 2;
/**
+ * Action indicating keyguard *can* start biometric authentiation.
+ */
+ private static final int BIOMETRIC_ACTION_START = 0;
+ /**
+ * Action indicating keyguard *can* stop biometric authentiation.
+ */
+ private static final int BIOMETRIC_ACTION_STOP = 1;
+ /**
+ * Action indicating keyguard *can* start or stop biometric authentiation.
+ */
+ private static final int BIOMETRIC_ACTION_UPDATE = 2;
+
+ /**
* Biometric state: During cancelling we got another request to start listening, so when we
* receive the cancellation done signal, we should start listening again.
*/
@@ -339,13 +352,13 @@
private final Runnable mFpCancelNotReceived = () -> {
Log.e(TAG, "Fp cancellation not received, transitioning to STOPPED");
mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
};
private final Runnable mFaceCancelNotReceived = () -> {
Log.e(TAG, "Face cancellation not received, transitioning to STOPPED");
mFaceRunningState = BIOMETRIC_STATE_STOPPED;
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_STOP);
};
private final Handler mHandler;
@@ -365,7 +378,7 @@
public void onChanged(boolean enabled, int userId) throws RemoteException {
mHandler.post(() -> {
mBiometricEnabledForUser.put(userId, enabled);
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
});
}
};
@@ -415,7 +428,6 @@
private final KeyguardListenQueue mListenModels = new KeyguardListenQueue();
private static int sCurrentUser;
- private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
public synchronized static void setCurrentUser(int currentUser) {
sCurrentUser = currentUser;
@@ -428,8 +440,17 @@
@Override
public void onTrustChanged(boolean enabled, int userId, int flags) {
Assert.isMainThread();
+ boolean wasTrusted = mUserHasTrust.get(userId, false);
mUserHasTrust.put(userId, enabled);
- updateBiometricListeningState();
+ // If there was no change in trusted state, make sure we are not authenticating.
+ // TrustManager sends an onTrustChanged whenever a user unlocks keyguard, for
+ // this reason we need to make sure to not authenticate.
+ if (wasTrusted == enabled) {
+ updateBiometricListeningState(BIOMETRIC_ACTION_STOP);
+ } else if (!enabled) {
+ updateBiometricListeningState(BIOMETRIC_ACTION_START);
+ }
+
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -594,7 +615,8 @@
*/
public void setCredentialAttempted() {
mCredentialAttempted = true;
- updateBiometricListeningState();
+ // Do not update face listening state in case of false authentication attempts.
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -602,7 +624,7 @@
*/
public void setKeyguardGoingAway(boolean goingAway) {
mKeyguardGoingAway = goingAway;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -610,7 +632,7 @@
*/
public void setKeyguardOccluded(boolean occluded) {
mKeyguardOccluded = occluded;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
@@ -622,7 +644,7 @@
*/
public void requestFaceAuthOnOccludingApp(boolean request) {
mOccludingAppRequestingFace = request;
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -633,7 +655,7 @@
*/
public void requestFingerprintAuthOnOccludingApp(boolean request) {
mOccludingAppRequestingFp = request;
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -641,7 +663,7 @@
*/
public void onCameraLaunched() {
mSecureCameraLaunched = true;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -676,7 +698,7 @@
}
// Don't send cancel if authentication succeeds
mFingerprintCancelSignal = null;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -772,7 +794,7 @@
Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
mHardwareFingerprintUnavailableRetryCount);
if (mFpm.isHardwareDetected()) {
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
mHardwareFingerprintUnavailableRetryCount++;
mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
@@ -792,7 +814,7 @@
if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
&& mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else {
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
@@ -801,16 +823,19 @@
mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
}
+ boolean lockedOutStateChanged = false;
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged |= !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
requireStrongAuthIfAllLockedOut();
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged |= !mFingerprintLockedOut;
mFingerprintLockedOut = true;
if (isUdfpsEnrolled()) {
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
}
@@ -820,9 +845,14 @@
cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
}
}
+
+ if (lockedOutStateChanged) {
+ notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ }
}
private void handleFingerprintLockoutReset() {
+ boolean changed = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
mFingerprintLockedOut = false;
mFingerprintLockedOutPermanent = false;
@@ -832,10 +862,15 @@
// that the events will arrive in a particular order. Add a delay here in case
// an unlock is in progress. In this is a normal unlock the extra delay won't
// be noticeable.
- mHandler.postDelayed(this::updateFingerprintListeningState,
- FINGERPRINT_LOCKOUT_RESET_DELAY_MS);
+ mHandler.postDelayed(() -> {
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ }, BIOMETRIC_LOCKOUT_RESET_DELAY_MS);
} else {
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ }
+
+ if (changed) {
+ notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
}
}
@@ -875,7 +910,7 @@
}
// Don't send cancel if authentication succeeds
mFaceCancelSignal = null;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -968,7 +1003,7 @@
public void run() {
Log.w(TAG, "Retrying face after HW unavailable, attempt " +
mHardwareFaceUnavailableRetryCount);
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
}
};
@@ -985,7 +1020,7 @@
if (msgId == FaceManager.FACE_ERROR_CANCELED
&& mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
} else {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
}
@@ -999,7 +1034,9 @@
}
}
+ boolean lockedOutStateChanged = false;
if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged = !mFaceLockedOutPermanent;
mFaceLockedOutPermanent = true;
requireStrongAuthIfAllLockedOut();
}
@@ -1011,11 +1048,23 @@
BiometricSourceType.FACE);
}
}
+
+ if (lockedOutStateChanged) {
+ notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ }
}
private void handleFaceLockoutReset() {
+ boolean changed = mFaceLockedOutPermanent;
mFaceLockedOutPermanent = false;
- updateFaceListeningState();
+
+ mHandler.postDelayed(() -> {
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+ }, BIOMETRIC_LOCKOUT_RESET_DELAY_MS);
+
+ if (changed) {
+ notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ }
}
private void setFaceRunningState(int faceRunningState) {
@@ -1237,6 +1286,16 @@
}
}
+ private void notifyLockedOutStateChanged(BiometricSourceType type) {
+ Assert.isMainThread();
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onLockedOutStateChanged(type);
+ }
+ }
+ }
+
public boolean isScreenOn() {
return mScreenOn;
}
@@ -1254,7 +1313,7 @@
@VisibleForTesting
void setAssistantVisible(boolean assistantVisible) {
mAssistantVisible = assistantVisible;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
static class DisplayClientState {
@@ -1593,7 +1652,7 @@
protected void handleStartedWakingUp() {
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
Assert.isMainThread();
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1614,7 +1673,7 @@
}
}
mGoingToSleep = true;
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
protected void handleFinishedGoingToSleep(int arg1) {
@@ -1626,7 +1685,7 @@
cb.onFinishedGoingToSleep(arg1);
}
}
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
private void handleScreenTurnedOn() {
@@ -1663,7 +1722,7 @@
cb.onDreamingStateChanged(mIsDreaming);
}
}
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
private void handleUserInfoChanged(int userId) {
@@ -1854,7 +1913,7 @@
setAssistantVisible((boolean) msg.obj);
break;
case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
break;
case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
updateLogoutEnabled();
@@ -1972,10 +2031,10 @@
@Override
public void onEnrollmentsChanged() {
- mainExecutor.execute(() -> updateBiometricListeningState());
+ mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE));
}
});
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
if (mFpm != null) {
mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
}
@@ -2089,12 +2148,12 @@
mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
}
- private void updateBiometricListeningState() {
- updateFingerprintListeningState();
- updateFaceListeningState();
+ private void updateBiometricListeningState(int action) {
+ updateFingerprintListeningState(action);
+ updateFaceListeningState(action);
}
- private void updateFingerprintListeningState() {
+ private void updateFingerprintListeningState(int action) {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
@@ -2106,8 +2165,16 @@
final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
if (runningOrRestarting && !shouldListenForFingerprint) {
+ if (action == BIOMETRIC_ACTION_START) {
+ Log.v(TAG, "Ignoring stopListeningForFingerprint()");
+ return;
+ }
stopListeningForFingerprint();
} else if (!runningOrRestarting && shouldListenForFingerprint) {
+ if (action == BIOMETRIC_ACTION_STOP) {
+ Log.v(TAG, "Ignoring startListeningForFingerprint()");
+ return;
+ }
startListeningForFingerprint();
}
}
@@ -2136,7 +2203,7 @@
return;
}
mAuthInterruptActive = active;
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -2147,7 +2214,7 @@
public void requestFaceAuth(boolean userInitiatedRequest) {
if (DEBUG) Log.d(TAG, "requestFaceAuth() userInitiated=" + userInitiatedRequest);
mIsFaceAuthUserRequested |= userInitiatedRequest;
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
}
public boolean isFaceAuthUserRequested() {
@@ -2161,7 +2228,7 @@
stopListeningForFace();
}
- private void updateFaceListeningState() {
+ private void updateFaceListeningState(int action) {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
@@ -2170,9 +2237,17 @@
mHandler.removeCallbacks(mRetryFaceAuthentication);
boolean shouldListenForFace = shouldListenForFace();
if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) {
+ if (action == BIOMETRIC_ACTION_START) {
+ Log.v(TAG, "Ignoring stopListeningForFace()");
+ return;
+ }
mIsFaceAuthUserRequested = false;
stopListeningForFace();
} else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING && shouldListenForFace) {
+ if (action == BIOMETRIC_ACTION_STOP) {
+ Log.v(TAG, "Ignoring startListeningForFace()");
+ return;
+ }
startListeningForFace();
}
}
@@ -2380,7 +2455,7 @@
mLockIconPressed = true;
final int userId = getCurrentUser();
mUserFaceAuthenticated.put(userId, null);
- updateFaceListeningState();
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
mStrongAuthTracker.onStrongAuthRequiredChanged(userId);
}
@@ -2454,6 +2529,10 @@
}
}
+ public boolean isFingerprintLockedOut() {
+ return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+ }
+
/**
* If biometrics hardware is available, not disabled, and user has enrolled templates.
* This does NOT check if the device is encrypted or in lockdown.
@@ -2552,7 +2631,7 @@
*/
private void handleDevicePolicyManagerStateChanged(int userId) {
Assert.isMainThread();
- updateFingerprintListeningState();
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
updateSecondaryLockscreenRequirement(userId);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2842,7 +2921,7 @@
cb.onKeyguardVisibilityChangedRaw(showing);
}
}
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
/** Notifies that the occluded state changed. */
@@ -2864,7 +2943,7 @@
*/
private void handleKeyguardReset() {
if (DEBUG) Log.d(TAG, "handleKeyguardReset");
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
}
@@ -2910,7 +2989,7 @@
cb.onKeyguardBouncerChanged(mBouncer);
}
}
- updateBiometricListeningState();
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
/**
@@ -3030,7 +3109,9 @@
public void setSwitchingUser(boolean switching) {
mSwitchingUser = switching;
// Since this comes in on a binder thread, we need to post if first
- mHandler.post(mUpdateBiometricListeningState);
+ mHandler.post(() -> {
+ updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+ });
}
private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 12431984..8170a81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -292,6 +292,11 @@
public void onStrongAuthStateChanged(int userId) { }
/**
+ * When the current user's locked out state changed.
+ */
+ public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) { }
+
+ /**
* Called when the dream's window state is changed.
* @param dreaming true if the dream's window has been created and is visible
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 68132f4..b2ecc614 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
@@ -30,6 +31,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -82,14 +84,18 @@
void updateColorAndBackgroundVisibility() {
if (mUseBackground && mLockIcon.getDrawable() != null) {
- mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.textColorPrimary);
+ mLockIconColor = ColorUtils.blendARGB(
+ Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
+ Color.WHITE,
+ mDozeAmount);
mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
mBgView.setAlpha(1f - mDozeAmount);
mBgView.setVisibility(View.VISIBLE);
} else {
- mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
- R.attr.wallpaperTextColorAccent);
+ mLockIconColor = ColorUtils.blendARGB(
+ Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
+ Color.WHITE,
+ mDozeAmount);
mBgView.setVisibility(View.GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 8a99728..251c1e6 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -121,7 +121,8 @@
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
- .setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI()));
+ .setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI()))
+ .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
// is separating this logic into newly creating SystemUITestsFactory.
@@ -140,7 +141,8 @@
.setStartingSurface(Optional.ofNullable(null))
.setTaskSurfaceHelper(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
- .setSizeCompatUI(Optional.ofNullable(null));
+ .setSizeCompatUI(Optional.ofNullable(null))
+ .setDragAndDrop(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (mInitializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
index c941d66..e4e0da6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
@@ -71,7 +71,9 @@
public void addListener(@NonNull T listener) {
Objects.requireNonNull(listener, "listener must be non-null");
- mListeners.add(listener);
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
if (mListeners.size() == 1) {
mContentResolver.registerContentObserver(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 794b9dd5..a10efa9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -158,12 +158,13 @@
@MainThread
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
- windowMagnificationController.enableWindowMagnification(scale, centerX,
- centerY, callback);
+ windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
+ magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 1bfa9c1..dc1e005 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -62,6 +62,8 @@
private final ValueAnimator mValueAnimator;
private final AnimationSpec mStartSpec = new AnimationSpec();
private final AnimationSpec mEndSpec = new AnimationSpec();
+ private float mMagnificationFrameOffsetRatioX = 0f;
+ private float mMagnificationFrameOffsetRatioY = 0f;
private final Context mContext;
// Called when the animation is ended successfully without cancelling or mStartSpec and
// mEndSpec are equal.
@@ -88,7 +90,8 @@
}
/**
- * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)}
+ * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float,
+ * float, float, IRemoteMagnificationAnimationCallback)}
* with transition animation. If the window magnification is not enabled, the scale will start
* from 1.0 and the center won't be changed during the animation. If {@link #mState} is
* {@code STATE_DISABLING}, the animation runs in reverse.
@@ -106,16 +109,48 @@
*/
void enableWindowMagnification(float scale, float centerX, float centerY,
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
+ enableWindowMagnification(scale, centerX, centerY, 0f, 0f, animationCallback);
+ }
+
+ /**
+ * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float,
+ * float, float, IRemoteMagnificationAnimationCallback)}
+ * with transition animation. If the window magnification is not enabled, the scale will start
+ * from 1.0 and the center won't be changed during the animation. If {@link #mState} is
+ * {@code STATE_DISABLING}, the animation runs in reverse.
+ *
+ * @param scale The target scale, or {@link Float#NaN} to leave unchanged.
+ * @param centerX The screen-relative X coordinate around which to center for magnification,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param centerY The screen-relative Y coordinate around which to center for magnification,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param magnificationFrameOffsetRatioX Indicate the X coordinate offset between
+ * frame position X and centerX
+ * @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset between
+ * frame position Y and centerY
+ * @param animationCallback Called when the transition is complete, the given arguments
+ * are as same as current values, or the transition is interrupted
+ * due to the new transition request.
+ *
+ * @see #onAnimationUpdate(ValueAnimator)
+ */
+ void enableWindowMagnification(float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
+ @Nullable IRemoteMagnificationAnimationCallback animationCallback) {
if (mController == null) {
return;
}
sendAnimationCallback(false);
+ mMagnificationFrameOffsetRatioX = magnificationFrameOffsetRatioX;
+ mMagnificationFrameOffsetRatioY = magnificationFrameOffsetRatioY;
+
// Enable window magnification without animation immediately.
if (animationCallback == null) {
if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
mValueAnimator.cancel();
}
- mController.enableWindowMagnification(scale, centerX, centerY);
+ mController.enableWindowMagnificationInternal(scale, centerX, centerY,
+ mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
setState(STATE_ENABLED);
return;
}
@@ -123,7 +158,8 @@
setupEnableAnimationSpecs(scale, centerX, centerY);
if (mEndSpec.equals(mStartSpec)) {
if (mState == STATE_DISABLED) {
- mController.enableWindowMagnification(scale, centerX, centerY);
+ mController.enableWindowMagnificationInternal(scale, centerX, centerY,
+ mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
} else if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
mValueAnimator.cancel();
}
@@ -273,7 +309,8 @@
mStartSpec.mCenterX + (mEndSpec.mCenterX - mStartSpec.mCenterX) * fract;
final float centerY =
mStartSpec.mCenterY + (mEndSpec.mCenterY - mStartSpec.mCenterY) * fract;
- mController.enableWindowMagnification(sentScale, centerX, centerY);
+ mController.enableWindowMagnificationInternal(sentScale, centerX, centerY,
+ mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
}
private static ValueAnimator newValueAnimator(Resources resources) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index 92cd8b1..2133da2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -49,11 +49,13 @@
}
@Override
- public void enableWindowMagnification(int displayId, float scale, float centerX,
- float centerY, IRemoteMagnificationAnimationCallback callback) {
+ public void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
+ IRemoteMagnificationAnimationCallback callback) {
mHandler.post(
() -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
- centerY, callback));
+ centerY, magnificationFrameOffsetRatioX,
+ magnificationFrameOffsetRatioY, callback));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 2507004..b064ba9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -95,17 +95,46 @@
@Surface.Rotation
@VisibleForTesting
int mRotation;
- private final Rect mMagnificationFrame = new Rect();
private final SurfaceControl.Transaction mTransaction;
private final WindowManager mWm;
private float mScale;
+ /**
+ * MagnificationFrame represents the bound of {@link #mMirrorSurface} and is constrained
+ * by the {@link #mMagnificationFrameBoundary}.
+ * We use MagnificationFrame to calculate the position of {@link #mMirrorView}.
+ * We combine MagnificationFrame with {@link #mMagnificationFrameOffsetX} and
+ * {@link #mMagnificationFrameOffsetY} to calculate the position of {@link #mSourceBounds}.
+ */
+ private final Rect mMagnificationFrame = new Rect();
private final Rect mTmpRect = new Rect();
+
+ /**
+ * MirrorViewBounds is the bound of the {@link #mMirrorView} which displays the magnified
+ * content.
+ * {@link #mMirrorView}'s center is equal to {@link #mMagnificationFrame}'s center.
+ */
private final Rect mMirrorViewBounds = new Rect();
+
+ /**
+ * SourceBound is the bound of the magnified region which projects the magnified content.
+ * SourceBound's center is equal to the parameters centerX and centerY in
+ * {@link WindowMagnificationController#enableWindowMagnificationInternal(float, float, float)}}
+ * but it is calculated from {@link #mMagnificationFrame}'s center in the runtime.
+ */
private final Rect mSourceBounds = new Rect();
+ /**
+ * The relation of centers between {@link #mSourceBounds} and {@link #mMagnificationFrame} is
+ * calculated in {@link #calculateSourceBounds(Rect, float)} and the equations are as following:
+ * MagnificationFrame = SourceBound (e.g., centerX & centerY) + MagnificationFrameOffset
+ * SourceBound = MagnificationFrame - MagnificationFrameOffset
+ */
+ private int mMagnificationFrameOffsetX = 0;
+ private int mMagnificationFrameOffsetY = 0;
+
// The root of the mirrored content
private SurfaceControl mMirrorSurface;
@@ -123,6 +152,7 @@
private final Runnable mMirrorViewRunnable;
private final Runnable mUpdateStateDescriptionRunnable;
private final Runnable mWindowInsetChangeRunnable;
+ // MirrorView is the mirror window which displays the magnified content.
private View mMirrorView;
private SurfaceView mMirrorSurfaceView;
private int mMirrorSurfaceMargin;
@@ -339,7 +369,7 @@
// window size changed not caused by rotation.
if (isWindowVisible() && reCreateWindow) {
deleteWindowMagnification();
- enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
+ enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN);
}
}
@@ -633,6 +663,26 @@
int top = displayFrame.top + (halfHeight - (int) (halfHeight / scale));
int bottom = displayFrame.bottom - (halfHeight - (int) (halfHeight / scale));
mSourceBounds.set(left, top, right, bottom);
+
+ // SourceBound's center is equal to center[X,Y] but calculated from MagnificationFrame's
+ // center. The relation between SourceBound and MagnificationFrame is as following:
+ // MagnificationFrame = SourceBound (center[X,Y]) + MagnificationFrameOffset
+ // SourceBound = MagnificationFrame - MagnificationFrameOffset
+ mSourceBounds.offset(-mMagnificationFrameOffsetX, -mMagnificationFrameOffsetY);
+
+ if (mSourceBounds.left < 0) {
+ mSourceBounds.offsetTo(0, mSourceBounds.top);
+ } else if (mSourceBounds.right > mWindowBounds.width()) {
+ mSourceBounds.offsetTo(mWindowBounds.width() - mSourceBounds.width(),
+ mSourceBounds.top);
+ }
+
+ if (mSourceBounds.top < 0) {
+ mSourceBounds.offsetTo(mSourceBounds.left, 0);
+ } else if (mSourceBounds.bottom > mWindowBounds.height()) {
+ mSourceBounds.offsetTo(mSourceBounds.left,
+ mWindowBounds.height() - mSourceBounds.height());
+ }
}
private void calculateMagnificationFrameBoundary() {
@@ -646,11 +696,31 @@
final int scaledWidth = (int) (halfWidth / mScale);
// The scaled half height of magnified region.
final int scaledHeight = (int) (halfHeight / mScale);
- final int exceededWidth = halfWidth - scaledWidth;
- final int exceededHeight = halfHeight - scaledHeight;
- mMagnificationFrameBoundary.set(-exceededWidth, -exceededHeight,
- mWindowBounds.width() + exceededWidth, mWindowBounds.height() + exceededHeight);
+ // MagnificationFrameBoundary constrain the space of MagnificationFrame, and it also has
+ // to leave enough space for SourceBound to magnify the whole screen space.
+ // However, there is an offset between SourceBound and MagnificationFrame.
+ // The relation between SourceBound and MagnificationFrame is as following:
+ // SourceBound = MagnificationFrame - MagnificationFrameOffset
+ // Therefore, we have to adjust the exceededBoundary based on the offset.
+ //
+ // We have to increase the offset space for the SourceBound edges which are located in
+ // the MagnificationFrame. For example, if the offsetX and offsetY are negative, which
+ // means SourceBound is at right-bottom size of MagnificationFrame, the left and top
+ // edges of SourceBound are located in MagnificationFrame. So, we have to leave extra
+ // offset space at left and top sides and don't have to leave extra space at right and
+ // bottom sides.
+ final int exceededLeft = Math.max(halfWidth - scaledWidth - mMagnificationFrameOffsetX, 0);
+ final int exceededRight = Math.max(halfWidth - scaledWidth + mMagnificationFrameOffsetX, 0);
+ final int exceededTop = Math.max(halfHeight - scaledHeight - mMagnificationFrameOffsetY, 0);
+ final int exceededBottom = Math.max(halfHeight - scaledHeight + mMagnificationFrameOffsetY,
+ 0);
+
+ mMagnificationFrameBoundary.set(
+ -exceededLeft,
+ -exceededTop,
+ mWindowBounds.width() + exceededRight,
+ mWindowBounds.height() + exceededBottom);
}
/**
@@ -711,24 +781,30 @@
}
/**
- * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)}
+ * Wraps {@link WindowMagnificationController#enableWindowMagnificationInternal(float, float,
+ * float, float, float)}
* with transition animation. If the window magnification is not enabled, the scale will start
* from 1.0 and the center won't be changed during the animation. If animator is
* {@code STATE_DISABLING}, the animation runs in reverse.
*
* @param scale The target scale, or {@link Float#NaN} to leave unchanged.
- * @param centerX The screen-relative X coordinate around which to center,
+ * @param centerX The screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
- * @param centerY The screen-relative Y coordinate around which to center,
+ * @param centerY The screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
+ * @param magnificationFrameOffsetRatioX Indicate the X coordinate offset
+ * between frame position X and centerX
+ * @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset
+ * between frame position Y and centerY
* @param animationCallback Called when the transition is complete, the given arguments
* are as same as current values, or the transition is interrupted
* due to the new transition request.
*/
void enableWindowMagnification(float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
- mAnimationController.enableWindowMagnification(scale, centerX,
- centerY, animationCallback);
+ mAnimationController.enableWindowMagnification(scale, centerX, centerY,
+ magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, animationCallback);
}
/**
@@ -738,21 +814,56 @@
* be consistent with the behavior of display magnification.
*
* @param scale the target scale, or {@link Float#NaN} to leave unchanged
- * @param centerX the screen-relative X coordinate around which to center,
+ * @param centerX the screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
- * @param centerY the screen-relative Y coordinate around which to center,
+ * @param centerY the screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
*/
- void enableWindowMagnification(float scale, float centerX, float centerY) {
+ void enableWindowMagnificationInternal(float scale, float centerX, float centerY) {
+ enableWindowMagnificationInternal(scale, centerX, centerY, Float.NaN, Float.NaN);
+ }
+
+ /**
+ * Enables window magnification with specified parameters. If the given scale is <strong>less
+ * than or equal to 1.0f<strong>, then
+ * {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to
+ * be consistent with the behavior of display magnification.
+ *
+ * @param scale the target scale, or {@link Float#NaN} to leave unchanged
+ * @param centerX the screen-relative X coordinate around which to center for magnification,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param centerY the screen-relative Y coordinate around which to center for magnification,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param magnificationFrameOffsetRatioX Indicate the X coordinate offset
+ * between frame position X and centerX,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset
+ * between frame position Y and centerY,
+ * or {@link Float#NaN} to leave unchanged.
+ */
+ void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY) {
if (Float.compare(scale, 1.0f) <= 0) {
deleteWindowMagnification();
return;
}
+ mMagnificationFrameOffsetX = Float.isNaN(magnificationFrameOffsetRatioX)
+ ? mMagnificationFrameOffsetX
+ : (int) (mMagnificationFrame.width() / 2 * magnificationFrameOffsetRatioX);
+ mMagnificationFrameOffsetY = Float.isNaN(magnificationFrameOffsetRatioY)
+ ? mMagnificationFrameOffsetY
+ : (int) (mMagnificationFrame.height() / 2 * magnificationFrameOffsetRatioY);
+
+ // The relation of centers between SourceBound and MagnificationFrame is as following:
+ // MagnificationFrame = SourceBound (e.g., centerX & centerY) + MagnificationFrameOffset
+ final float newMagnificationFrameCenterX = centerX + mMagnificationFrameOffsetX;
+ final float newMagnificationFrameCenterY = centerY + mMagnificationFrameOffsetY;
+
final float offsetX = Float.isNaN(centerX) ? 0
- : centerX - mMagnificationFrame.exactCenterX();
+ : newMagnificationFrameCenterX - mMagnificationFrame.exactCenterX();
final float offsetY = Float.isNaN(centerY) ? 0
- : centerY - mMagnificationFrame.exactCenterY();
+ : newMagnificationFrameCenterY - mMagnificationFrame.exactCenterY();
mScale = Float.isNaN(scale) ? mScale : scale;
calculateMagnificationFrameBoundary();
@@ -774,7 +885,7 @@
if (mAnimationController.isAnimating() || !isWindowVisible() || mScale == scale) {
return;
}
- enableWindowMagnification(scale, Float.NaN, Float.NaN);
+ enableWindowMagnificationInternal(scale, Float.NaN, Float.NaN);
mHandler.removeCallbacks(mUpdateStateDescriptionRunnable);
mHandler.postDelayed(mUpdateStateDescriptionRunnable, UPDATE_STATE_DESCRIPTION_DELAY_MS);
}
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 cff6cf1..cc5a792 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -98,7 +98,8 @@
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- init();
+ mIsKeyguardVisible = false;
+ mIsAccessibilityManagerServiceReady = false;
}
/**
@@ -124,9 +125,8 @@
handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
}
- private void init() {
- mIsKeyguardVisible = false;
- mIsAccessibilityManagerServiceReady = false;
+ /** Initializes the AccessibilityFloatingMenuController configurations. */
+ public void init() {
mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
mBtnTargets = mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
registerContentObservers();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 0ff3dbc..3f077f5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -163,7 +163,10 @@
private boolean mOnFingerDown;
private boolean mAttemptedToDismissKeyguard;
private Set<Callback> mCallbacks = new HashSet<>();
- private final VibrationEffect mLowTick;
+
+ // by default, use low tick
+ private int mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
+ private final VibrationEffect mTick;
@VisibleForTesting
public static final VibrationAttributes VIBRATION_ATTRIBUTES =
@@ -571,7 +574,7 @@
mConfigurationController = configurationController;
mSystemClock = systemClock;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- mLowTick = lowTick();
+ mTick = lowTick();
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
@@ -607,22 +610,31 @@
}
private VibrationEffect lowTick() {
+ boolean useLowTickDefault = mContext.getResources()
+ .getBoolean(R.bool.config_udfpsUseLowTick);
+ if (Settings.Global.getFloat(
+ mContext.getContentResolver(),
+ "tick-low", useLowTickDefault ? 1 : 0) == 0) {
+ mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK;
+ }
float tickIntensity = Settings.Global.getFloat(
- mContext.getContentResolver(), "low-tick-intensity", .5f);
- VibrationEffect.Composition composition = VibrationEffect.startComposition();
- composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
- tickIntensity, 0);
+ mContext.getContentResolver(),
+ "tick-intensity",
+ mContext.getResources().getFloat(R.dimen.config_udfpsTickIntensity));
int tickDelay = Settings.Global.getInt(
- mContext.getContentResolver(), "low-tick-delay", 25);
+ mContext.getContentResolver(),
+ "tick-delay",
+ mContext.getResources().getInteger(R.integer.config_udfpsTickDelay));
+
+ VibrationEffect.Composition composition = VibrationEffect.startComposition();
+ composition.addPrimitive(mPrimitiveTick, tickIntensity, 0);
int primitives = 1000 / tickDelay;
float[] rampUp = new float[]{.48f, .58f, .69f, .83f};
for (int i = 0; i < rampUp.length; i++) {
- composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
- tickIntensity * rampUp[i], tickDelay);
+ composition.addPrimitive(mPrimitiveTick, tickIntensity * rampUp[i], tickDelay);
}
for (int i = rampUp.length; i < primitives; i++) {
- composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
- tickIntensity, tickDelay);
+ composition.addPrimitive(mPrimitiveTick, tickIntensity, tickDelay);
}
return composition.compose();
}
@@ -636,7 +648,7 @@
mVibrator.vibrate(
Process.myUid(),
mContext.getOpPackageName(),
- mLowTick,
+ mTick,
"udfps-onStart-tick",
VIBRATION_ATTRIBUTES);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 223eb78..8f4d6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -255,7 +255,6 @@
private void maybeShowInputBouncer() {
if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
mKeyguardViewManager.showBouncer(true);
- mKeyguardViewManager.resetAlternateAuth(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
new file mode 100644
index 0000000..b5d81f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.biometrics.dagger
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.concurrency.ThreadFactory
+import dagger.Module
+import dagger.Provides
+import java.util.concurrent.Executor
+import javax.inject.Qualifier
+
+/**
+ * Dagger module for all things biometric.
+ */
+@Module
+object BiometricsModule {
+
+ /** Background [Executor] for HAL related operations. */
+ @Provides
+ @SysUISingleton
+ @JvmStatic
+ @BiometricsBackground
+ fun providesPluginExecutor(threadFactory: ThreadFactory): Executor =
+ threadFactory.buildExecutorOnNewThread("biometrics")
+}
+
+/**
+ * Background executor for HAL operations that are latency sensitive but too
+ * slow to run on the main thread. Prefer the shared executors, such as
+ * [com.android.systemui.dagger.qualifiers.Background] when a HAL is not directly involved.
+ */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class BiometricsBackground
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
index 99c311e..53586f5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
@@ -23,6 +23,8 @@
import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Optional;
+
/**
* {@link CommunalSource} defines an interface for working with a source for communal data. Clients
* may request a communal surface that can be shown within a {@link android.view.SurfaceView}.
@@ -30,6 +32,28 @@
*/
public interface CommunalSource {
/**
+ * {@link Connector} defines an interface for {@link CommunalSource} instances to be generated.
+ */
+ interface Connector {
+ ListenableFuture<Optional<CommunalSource>> connect();
+ }
+
+ /**
+ * The {@link Observer} interface specifies an entity which {@link CommunalSource} listeners
+ * can be informed of changes to the source, which will require updating. Note that this deals
+ * with changes to the source itself, not content which will be updated through the
+ * {@link CommunalSource} interface.
+ */
+ interface Observer {
+ interface Callback {
+ void onSourceChanged();
+ }
+
+ void addCallback(Callback callback);
+ void removeCallback(Callback callback);
+ }
+
+ /**
* {@link CommunalViewResult} is handed back from {@link #requestCommunalView(Context)} and
* contains the view to be displayed and its associated controller.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
new file mode 100644
index 0000000..1044239
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
@@ -0,0 +1,175 @@
+/*
+ * 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.communal;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link CommunalSourcePrimer} is responsible for priming SystemUI with a pre-configured
+ * Communal source. The SystemUI service binds to the component to retrieve the
+ * {@link CommunalSource}. {@link CommunalSourcePrimer} has no effect
+ * if there is no pre-defined value.
+ */
+@SysUISingleton
+public class CommunalSourcePrimer extends CoreStartable {
+ private static final String TAG = "CommunalSourcePrimer";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final SystemClock mSystemClock;
+ private final DelayableExecutor mMainExecutor;
+ private final CommunalSourceMonitor mMonitor;
+ private final int mBaseReconnectDelayMs;
+ private final int mMaxReconnectAttempts;
+ private final int mMinConnectionDuration;
+
+ private int mReconnectAttempts = 0;
+ private Runnable mCurrentReconnectCancelable;
+ private ListenableFuture<Optional<CommunalSource>> mGetSourceFuture;
+
+ private final Optional<CommunalSource.Connector> mConnector;
+ private final Optional<CommunalSource.Observer> mObserver;
+
+ private final Runnable mConnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCurrentReconnectCancelable = null;
+ connect();
+ }
+ };
+
+ @Inject
+ public CommunalSourcePrimer(Context context, @Main Resources resources,
+ SystemClock clock,
+ DelayableExecutor mainExecutor,
+ CommunalSourceMonitor monitor,
+ Optional<CommunalSource.Connector> connector,
+ Optional<CommunalSource.Observer> observer) {
+ super(context);
+ mSystemClock = clock;
+ mMainExecutor = mainExecutor;
+ mMonitor = monitor;
+ mConnector = connector;
+ mObserver = observer;
+
+ mMaxReconnectAttempts = resources.getInteger(
+ R.integer.config_communalSourceMaxReconnectAttempts);
+ mBaseReconnectDelayMs = resources.getInteger(
+ R.integer.config_communalSourceReconnectBaseDelay);
+ mMinConnectionDuration = resources.getInteger(
+ R.integer.config_connectionMinDuration);
+ }
+
+ @Override
+ public void start() {
+ }
+
+ private void initiateConnectionAttempt() {
+ // Reset attempts
+ mReconnectAttempts = 0;
+ mMonitor.setSource(null);
+
+ // The first attempt is always a direct invocation rather than delayed.
+ connect();
+ }
+
+ private void scheduleConnectionAttempt() {
+ // always clear cancelable if present.
+ if (mCurrentReconnectCancelable != null) {
+ mCurrentReconnectCancelable.run();
+ mCurrentReconnectCancelable = null;
+ }
+
+ if (mReconnectAttempts >= mMaxReconnectAttempts) {
+ if (DEBUG) {
+ Log.d(TAG, "exceeded max connection attempts.");
+ }
+ return;
+ }
+
+ final long reconnectDelayMs =
+ (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts);
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "scheduling connection attempt in " + reconnectDelayMs + "milliseconds");
+ }
+
+ mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable,
+ reconnectDelayMs);
+
+ mReconnectAttempts++;
+ }
+
+ @Override
+ protected void onBootCompleted() {
+ if (mObserver.isPresent()) {
+ mObserver.get().addCallback(() -> initiateConnectionAttempt());
+ }
+ initiateConnectionAttempt();
+ }
+
+ private void connect() {
+ if (DEBUG) {
+ Log.d(TAG, "attempting to communal to communal source");
+ }
+
+ if (mGetSourceFuture != null) {
+ if (DEBUG) {
+ Log.d(TAG, "canceling in-flight connection");
+ }
+ mGetSourceFuture.cancel(true);
+ }
+
+ mGetSourceFuture = mConnector.get().connect();
+ mGetSourceFuture.addListener(() -> {
+ try {
+ final long startTime = mSystemClock.currentTimeMillis();
+ Optional<CommunalSource> result = mGetSourceFuture.get();
+ if (result.isPresent()) {
+ final CommunalSource source = result.get();
+ source.addCallback(() -> {
+ if (mSystemClock.currentTimeMillis() - startTime > mMinConnectionDuration) {
+ initiateConnectionAttempt();
+ } else {
+ scheduleConnectionAttempt();
+ }
+ });
+ mMonitor.setSource(source);
+ } else {
+ scheduleConnectionAttempt();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }, mMainExecutor);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 5fdf026..38e4d78 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -23,6 +23,7 @@
import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.media.taptotransfer.MediaTttChipController;
import com.android.systemui.people.PeopleProvider;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
@@ -32,6 +33,7 @@
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
@@ -111,6 +113,9 @@
@BindsInstance
Builder setSizeCompatUI(Optional<SizeCompatUI> s);
+ @BindsInstance
+ Builder setDragAndDrop(Optional<DragAndDrop> d);
+
SysUIComponent build();
}
@@ -126,6 +131,8 @@
c.getUnfoldTransitionWallpaperController().init();
});
getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init());
+ // No init method needed, just needs to be gotten so that it's created.
+ getMediaTttChipController();
}
/**
@@ -172,6 +179,9 @@
*/
Optional<NaturalRotationUnfoldProgressProvider> getNaturalRotationUnfoldProgressProvider();
+ /** */
+ Optional<MediaTttChipController> getMediaTttChipController();
+
/**
* Member injection into the supplied argument.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a64351f..1d17fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -30,6 +30,7 @@
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.UdfpsHbmProvider;
+import com.android.systemui.biometrics.dagger.BiometricsModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.controls.dagger.ControlsModule;
@@ -66,7 +67,6 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -101,6 +101,7 @@
@Module(includes = {
AppOpsModule.class,
AssistModule.class,
+ BiometricsModule.class,
ClockModule.class,
CommunalModule.class,
DreamModule.class,
@@ -127,7 +128,6 @@
},
subcomponents = {
StatusBarComponent.class,
- StatusBarFragmentComponent.class,
NotificationRowComponent.class,
DozeComponent.class,
ExpandableNotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 543ba8f..90a3ad2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -29,6 +29,7 @@
import com.android.wm.shell.dagger.WMShellModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
@@ -119,4 +120,7 @@
@WMSingleton
SizeCompatUI getSizeCompatUI();
+
+ @WMSingleton
+ DragAndDrop getDragAndDrop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt
new file mode 100644
index 0000000..42f3512
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.fgsmanager
+
+import android.content.Context
+import android.os.Bundle
+import android.text.format.DateUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.annotation.GuardedBy
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.fgsmanager.FgsManagerDialogController.RunningApp
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.time.SystemClock
+import java.util.concurrent.Executor
+
+/**
+ * Dialog which shows a list of running foreground services and offers controls to them
+ */
+class FgsManagerDialog(
+ context: Context,
+ private val executor: Executor,
+ @Background private val backgroundExecutor: Executor,
+ private val systemClock: SystemClock,
+ private val fgsManagerDialogController: FgsManagerDialogController
+) : SystemUIDialog(context, R.style.Theme_SystemUI_Dialog) {
+
+ private val appListRecyclerView: RecyclerView = RecyclerView(this.context)
+ private val adapter: AppListAdapter = AppListAdapter()
+
+ init {
+ setTitle(R.string.fgs_manager_dialog_title)
+ setView(appListRecyclerView)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ appListRecyclerView.layoutManager = LinearLayoutManager(context)
+ fgsManagerDialogController.registerDialogForChanges(
+ object : FgsManagerDialogController.FgsManagerDialogCallback {
+ override fun onRunningAppsChanged(apps: List<RunningApp>) {
+ executor.execute {
+ adapter.setData(apps)
+ }
+ }
+ }
+ )
+ appListRecyclerView.adapter = adapter
+ backgroundExecutor.execute { adapter.setData(fgsManagerDialogController.runningAppList) }
+ }
+
+ private inner class AppListAdapter : RecyclerView.Adapter<AppItemViewHolder>() {
+ private val lock = Any()
+
+ @GuardedBy("lock")
+ private val data: MutableList<RunningApp> = ArrayList()
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppItemViewHolder {
+ return AppItemViewHolder(LayoutInflater.from(context)
+ .inflate(R.layout.fgs_manager_app_item, parent, false))
+ }
+
+ override fun onBindViewHolder(holder: AppItemViewHolder, position: Int) {
+ var runningApp: RunningApp
+ synchronized(lock) {
+ runningApp = data[position]
+ }
+ with(holder) {
+ iconView.setImageDrawable(runningApp.mIcon)
+ appLabelView.text = runningApp.mAppLabel
+ durationView.text = DateUtils.formatDuration(
+ Math.max(systemClock.elapsedRealtime() - runningApp.mTimeStarted, 60000),
+ DateUtils.LENGTH_MEDIUM)
+ stopButton.setOnClickListener {
+ fgsManagerDialogController
+ .stopAllFgs(runningApp.mUserId, runningApp.mPackageName)
+ }
+ }
+ }
+
+ override fun getItemCount(): Int {
+ synchronized(lock) { return data.size }
+ }
+
+ fun setData(newData: List<RunningApp>) {
+ var oldData: List<RunningApp>
+ synchronized(lock) {
+ oldData = ArrayList(data)
+ data.clear()
+ data.addAll(newData)
+ }
+
+ DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+ override fun getOldListSize(): Int {
+ return oldData.size
+ }
+
+ override fun getNewListSize(): Int {
+ return newData.size
+ }
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int):
+ Boolean {
+ return oldData[oldItemPosition] == newData[newItemPosition]
+ }
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int):
+ Boolean {
+ return true // TODO, look into updating the time subtext
+ }
+ }).dispatchUpdatesTo(this)
+ }
+ }
+
+ private class AppItemViewHolder(parent: View) : RecyclerView.ViewHolder(parent) {
+ val appLabelView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_label)
+ val durationView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_duration)
+ val iconView: ImageView = parent.requireViewById(R.id.fgs_manager_app_item_icon)
+ val stopButton: Button = parent.requireViewById(R.id.fgs_manager_app_item_stop_button)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt
new file mode 100644
index 0000000..159ed39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.fgsmanager
+
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.graphics.drawable.Drawable
+import android.os.Handler
+import android.os.UserHandle
+import android.util.ArrayMap
+import android.util.Log
+import androidx.annotation.GuardedBy
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.policy.RunningFgsController
+import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime
+import javax.inject.Inject
+
+/**
+ * Controls events relevant to FgsManagerDialog
+ */
+class FgsManagerDialogController @Inject constructor(
+ private val packageManager: PackageManager,
+ @Background private val backgroundHandler: Handler,
+ private val runningFgsController: RunningFgsController
+) : RunningFgsController.Callback {
+ private val lock = Any()
+ private val clearCacheToken = Any()
+
+ @GuardedBy("lock")
+ private var runningApps: Map<UserPackageTime, RunningApp>? = null
+ @GuardedBy("lock")
+ private var listener: FgsManagerDialogCallback? = null
+
+ interface FgsManagerDialogCallback {
+ fun onRunningAppsChanged(apps: List<RunningApp>)
+ }
+
+ data class RunningApp(
+ val mUserId: Int,
+ val mPackageName: String,
+ val mAppLabel: CharSequence,
+ val mIcon: Drawable,
+ val mTimeStarted: Long
+ )
+
+ val runningAppList: List<RunningApp>
+ get() {
+ synchronized(lock) {
+ if (runningApps == null) {
+ onFgsPackagesChangedLocked(runningFgsController.getPackagesWithFgs())
+ }
+ return convertToRunningAppList(runningApps!!)
+ }
+ }
+
+ fun registerDialogForChanges(callback: FgsManagerDialogCallback) {
+ synchronized(lock) {
+ runningFgsController.addCallback(this)
+ listener = callback
+ backgroundHandler.removeCallbacksAndMessages(clearCacheToken)
+ }
+ }
+
+ fun onFinishDialog() {
+ synchronized(lock) {
+ listener = null
+ // Keep data such as icons cached for some time since loading can be slow
+ backgroundHandler.postDelayed(
+ {
+ synchronized(lock) {
+ runningFgsController.removeCallback(this)
+ runningApps = null
+ }
+ }, clearCacheToken, RUNNING_APP_CACHE_TIMEOUT_MILLIS)
+ }
+ }
+
+ private fun onRunningAppsChanged(apps: ArrayMap<UserPackageTime, RunningApp>) {
+ listener?.let {
+ backgroundHandler.post { it.onRunningAppsChanged(convertToRunningAppList(apps)) }
+ }
+ }
+
+ override fun onFgsPackagesChanged(packages: List<UserPackageTime>) {
+ backgroundHandler.post {
+ synchronized(lock) { onFgsPackagesChangedLocked(packages) }
+ }
+ }
+
+ /**
+ * Run on background thread
+ */
+ private fun onFgsPackagesChangedLocked(packages: List<UserPackageTime>) {
+ val newRunningApps = ArrayMap<UserPackageTime, RunningApp>()
+ for (packageWithFgs in packages) {
+ val ra = runningApps?.get(packageWithFgs)
+ if (ra == null) {
+ val userId = packageWithFgs.userId
+ val packageName = packageWithFgs.packageName
+ try {
+ val ai = packageManager.getApplicationInfo(packageName, 0)
+ var icon = packageManager.getApplicationIcon(ai)
+ icon = packageManager.getUserBadgedIcon(icon,
+ UserHandle.of(userId))
+ val label = packageManager.getApplicationLabel(ai)
+ newRunningApps[packageWithFgs] = RunningApp(userId, packageName,
+ label, icon, packageWithFgs.startTimeMillis)
+ } catch (e: NameNotFoundException) {
+ Log.e(LOG_TAG,
+ "Application info not found: $packageName", e)
+ }
+ } else {
+ newRunningApps[packageWithFgs] = ra
+ }
+ }
+ runningApps = newRunningApps
+ onRunningAppsChanged(newRunningApps)
+ }
+
+ fun stopAllFgs(userId: Int, packageName: String) {
+ runningFgsController.stopFgs(userId, packageName)
+ }
+
+ companion object {
+ private val LOG_TAG = FgsManagerDialogController::class.java.simpleName
+ private const val RUNNING_APP_CACHE_TIMEOUT_MILLIS: Long = 20_000
+
+ private fun convertToRunningAppList(apps: Map<UserPackageTime, RunningApp>):
+ List<RunningApp> {
+ val result = mutableListOf<RunningApp>()
+ result.addAll(apps.values)
+ result.sortWith { a: RunningApp, b: RunningApp ->
+ b.mTimeStarted.compareTo(a.mTimeStarted)
+ }
+ return result
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt
new file mode 100644
index 0000000..2874929
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.fgsmanager
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.animation.DialogLaunchAnimator
+import android.content.DialogInterface
+import android.view.View
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.time.SystemClock
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Factory to create [FgsManagerDialog] instances
+ */
+@SysUISingleton
+class FgsManagerDialogFactory
+@Inject constructor(
+ private val context: Context,
+ @Main private val executor: Executor,
+ @Background private val backgroundExecutor: Executor,
+ private val systemClock: SystemClock,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val fgsManagerDialogController: FgsManagerDialogController
+) {
+
+ val lock = Any()
+
+ companion object {
+ private var fgsManagerDialog: FgsManagerDialog? = null
+ }
+
+ /**
+ * Creates the dialog if it doesn't exist
+ */
+ fun create(viewLaunchedFrom: View?) {
+ if (fgsManagerDialog == null) {
+ fgsManagerDialog = FgsManagerDialog(context, executor, backgroundExecutor,
+ systemClock, fgsManagerDialogController)
+ fgsManagerDialog!!.setOnDismissListener { i: DialogInterface? ->
+ fgsManagerDialogController.onFinishDialog()
+ fgsManagerDialog = null
+ }
+ dialogLaunchAnimator.showFromView(fgsManagerDialog!!, viewLaunchedFrom!!)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index a1413f9..458cdc1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -46,6 +46,10 @@
public static final BooleanFlag NOTIFICATION_UPDATES =
new BooleanFlag(102, true);
+ public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
+ new BooleanFlag(103, false);
+
+
/***************************************/
// 200 - keyguard/lockscreen
public static final BooleanFlag KEYGUARD_LAYOUT =
@@ -114,6 +118,10 @@
public static final BooleanFlag MONET =
new BooleanFlag(800, true, R.bool.flag_monet);
+ /***************************************/
+ // 900 - media
+ public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, false);
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 2f7c8ba..ff14064 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -31,8 +31,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -56,6 +54,7 @@
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -80,8 +79,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -112,7 +109,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
-import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
@@ -123,6 +120,7 @@
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -239,6 +237,7 @@
private int mSmallestScreenWidthDp;
private final Optional<StatusBar> mStatusBarOptional;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -346,7 +345,8 @@
@Main Handler handler,
PackageManager packageManager,
Optional<StatusBar> statusBarOptional,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -377,6 +377,7 @@
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -435,11 +436,14 @@
}
/**
- * Show the global actions dialog (creating if necessary)
+ * Show the global actions dialog (creating if necessary) or hide it if it's already showing.
*
- * @param keyguardShowing True if keyguard is showing
+ * @param keyguardShowing True if keyguard is showing
+ * @param isDeviceProvisioned True if device is provisioned
+ * @param view The view from which we should animate the dialog when showing it
*/
- public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
+ public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
+ @Nullable View view) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null && mDialog.isShowing()) {
@@ -451,7 +455,7 @@
mDialog.dismiss();
mDialog = null;
} else {
- handleShow();
+ handleShow(view);
}
}
@@ -483,7 +487,7 @@
}
}
- protected void handleShow() {
+ protected void handleShow(@Nullable View view) {
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
@@ -493,8 +497,13 @@
attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mDialog.getWindow().setAttributes(attrs);
// Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
- mDialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);
- mDialog.show();
+ mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
+
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(mDialog, view);
+ } else {
+ mDialog.show();
+ }
mWindowManagerFuncs.onGlobalActionsShown();
}
@@ -643,7 +652,7 @@
}
}
- protected void onRotate() {
+ protected void onRefresh() {
// re-allocate actions between main and overflow lists
this.createActionItems();
}
@@ -667,7 +676,7 @@
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
+ mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
dialog.setOnDismissListener(this);
@@ -702,14 +711,6 @@
}
@Override
- public void onUiModeChanged() {
- mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
- if (mDialog != null && mDialog.isShowing()) {
- mDialog.refreshDialog();
- }
- }
-
- @Override
public void onConfigChanged(Configuration newConfig) {
if (mDialog != null && mDialog.isShowing()
&& (newConfig.smallestScreenWidthDp != mSmallestScreenWidthDp)) {
@@ -717,6 +718,7 @@
mDialog.refreshDialog();
}
}
+
/**
* Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
* called when the quick access wallet requests dismissal.
@@ -1363,6 +1365,10 @@
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1379,6 +1385,10 @@
if (mDialog != null) {
// don't dismiss the dialog if we're opening the power options menu
if (!(item instanceof PowerOptionsAction)) {
+ // Usually clicking an item shuts down the phone, locks, or starts an
+ // activity. We don't want to animate back into the power button when that
+ // happens, so we disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
}
} else {
@@ -1446,6 +1456,10 @@
final Action action = getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1459,6 +1473,10 @@
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -1510,6 +1528,10 @@
final Action action = getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1523,6 +1545,10 @@
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -2066,7 +2092,9 @@
case MESSAGE_DISMISS:
if (mDialog != null) {
if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
- mDialog.completeDismiss();
+ // Hide instantly.
+ mDialog.hide();
+ mDialog.dismiss();
} else {
mDialog.dismiss();
}
@@ -2113,7 +2141,7 @@
}
@VisibleForTesting
- static class ActionsDialogLite extends Dialog implements DialogInterface,
+ static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
ColorExtractor.OnColorsChangedListener {
protected final Context mContext;
@@ -2126,13 +2154,12 @@
protected Drawable mBackgroundDrawable;
protected final SysuiColorExtractor mColorExtractor;
private boolean mKeyguardShowing;
- protected boolean mShowing;
protected float mScrimAlpha;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
protected final SysUiState mSysUiState;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
- protected final Runnable mOnRotateCallback;
+ protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
private Optional<StatusBar> mStatusBarOptional;
@@ -2151,7 +2178,7 @@
}
@Override
- public boolean onSingleTapConfirmed(MotionEvent e) {
+ public boolean onSingleTapUp(MotionEvent e) {
// Close without opening shade
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
cancel();
@@ -2189,11 +2216,13 @@
MyOverflowAdapter overflowAdapter,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
+ SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
Optional<StatusBar> statusBarOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
- super(context, themeRes);
+ // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
+ // dismiss this dialog when the device is locked.
+ super(context, themeRes, false /* dismissOnDeviceLock */);
mContext = context;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
@@ -2202,36 +2231,32 @@
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mSysUiState = sysuiState;
- mOnRotateCallback = onRotateCallback;
+ mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
-
mGestureDetector = new GestureDetector(mContext, mGestureListener);
+ }
- // Window initialization
- Window window = getWindow();
- window.requestFeature(Window.FEATURE_NO_TITLE);
- // Inflate the decor view, so the attributes below are not overwritten by the theme.
- window.getDecorView();
- window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- window.setLayout(MATCH_PARENT, MATCH_PARENT);
- window.addFlags(
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- window.getAttributes().setFitInsetsTypes(0 /* types */);
- setTitle(R.string.global_actions);
-
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
initializeLayout();
}
@Override
+ protected int getWidth() {
+ return MATCH_PARENT;
+ }
+
+ @Override
+ protected int getHeight() {
+ return MATCH_PARENT;
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
}
@@ -2371,7 +2396,8 @@
message.animate()
.alpha(0f)
.setDuration(TOAST_FADE_TIME)
- .setStartDelay(visibleTime);
+ .setStartDelay(visibleTime)
+ .setListener(null);
}
});
}
@@ -2423,122 +2449,32 @@
@Override
public void show() {
super.show();
- // split this up so we can override but still call Dialog.show
- showDialog();
- }
-
- protected void showDialog() {
- mShowing = true;
mNotificationShadeWindowController.setRequestTopUi(true, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
.commitUpdate(mContext.getDisplayId());
-
- ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
- root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
- root.setPadding(windowInsets.getStableInsetLeft(),
- windowInsets.getStableInsetTop(),
- windowInsets.getStableInsetRight(),
- windowInsets.getStableInsetBottom());
- return WindowInsets.CONSUMED;
- });
-
- mBackgroundDrawable.setAlpha(0);
- float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
- ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f);
- alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- alphaAnimator.setDuration(183);
- alphaAnimator.addUpdateListener((animation) -> {
- float animatedValue = animation.getAnimatedFraction();
- int alpha = (int) (animatedValue * mScrimAlpha * 255);
- mBackgroundDrawable.setAlpha(alpha);
- });
-
- ObjectAnimator xAnimator =
- ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f);
- xAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- xAnimator.setDuration(350);
-
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(alphaAnimator, xAnimator);
- animatorSet.start();
}
@Override
public void dismiss() {
- dismissWithAnimation(() -> {
- dismissInternal();
- });
- }
+ dismissOverflow();
+ dismissPowerOptions();
- protected void dismissInternal() {
- mContainer.setTranslationX(0);
- ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "alpha", 1f, 0f);
- alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- alphaAnimator.setDuration(233);
- alphaAnimator.addUpdateListener((animation) -> {
- float animatedValue = 1f - animation.getAnimatedFraction();
- int alpha = (int) (animatedValue * mScrimAlpha * 255);
- mBackgroundDrawable.setAlpha(alpha);
- });
-
- float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
- ObjectAnimator xAnimator =
- ObjectAnimator.ofFloat(mContainer, "translationX", 0f, xOffset);
- xAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- xAnimator.setDuration(350);
-
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(alphaAnimator, xAnimator);
- animatorSet.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- completeDismiss();
- }
- });
-
- animatorSet.start();
-
- // close first, as popup windows will not fade during the animation
- dismissOverflow(false);
- dismissPowerOptions(false);
- }
-
- void dismissWithAnimation(Runnable animation) {
- if (!mShowing) {
- return;
- }
- mShowing = false;
- animation.run();
- }
-
- protected void completeDismiss() {
- mShowing = false;
- dismissOverflow(true);
- dismissPowerOptions(true);
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
+
super.dismiss();
}
- protected final void dismissOverflow(boolean immediate) {
+ protected final void dismissOverflow() {
if (mOverflowPopup != null) {
- if (immediate) {
- mOverflowPopup.dismissImmediate();
- } else {
- mOverflowPopup.dismiss();
- }
+ mOverflowPopup.dismiss();
}
}
- protected final void dismissPowerOptions(boolean immediate) {
+ protected final void dismissPowerOptions() {
if (mPowerOptionsDialog != null) {
- if (immediate) {
- mPowerOptionsDialog.dismiss();
- } else {
- mPowerOptionsDialog.dismiss();
- }
+ mPowerOptionsDialog.dismiss();
}
}
@@ -2574,20 +2510,18 @@
}
public void refreshDialog() {
- // ensure dropdown menus are dismissed before re-initializing the dialog
- dismissOverflow(true);
- dismissPowerOptions(true);
+ mOnRefreshCallback.run();
- // re-create dialog
- initializeLayout();
+ // Dismiss the dropdown menus.
+ dismissOverflow();
+ dismissPowerOptions();
+
+ // Update the list as the max number of items per row has probably changed.
mGlobalActionsLayout.updateList();
}
public void onRotate(int from, int to) {
- if (mShowing) {
- mOnRotateCallback.run();
- refreshDialog();
- }
+ refreshDialog();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c4508e0..96ae646 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -82,7 +82,7 @@
if (mDisabled) return;
mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
- mDeviceProvisionedController.isDeviceProvisioned());
+ mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e97e762..fbc9ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -793,7 +793,8 @@
return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
} else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
- } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
+ } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
+ || mUpdateMonitor.isFingerprintLockedOut())) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
@@ -820,6 +821,7 @@
private final KeyguardStateController mKeyguardStateController;
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
+ private final InteractionJankMonitor mInteractionJankMonitor;
private boolean mWallpaperSupportsAmbientMode;
/**
@@ -845,7 +847,8 @@
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
- Lazy<NotificationShadeDepthController> notificationShadeDepthController) {
+ Lazy<NotificationShadeDepthController> notificationShadeDepthController,
+ InteractionJankMonitor interactionJankMonitor) {
super(context);
mFalsingCollector = falsingCollector;
mLockPatternUtils = lockPatternUtils;
@@ -882,6 +885,7 @@
mKeyguardStateController = keyguardStateController;
mKeyguardUnlockAnimationControllerLazy = keyguardUnlockAnimationControllerLazy;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+ mInteractionJankMonitor = interactionJankMonitor;
}
public void userActivity() {
@@ -2245,8 +2249,7 @@
onKeyguardExitFinished();
mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
0 /* fadeoutDuration */);
- InteractionJankMonitor.getInstance()
- .end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
@Override
@@ -2255,7 +2258,7 @@
}
};
try {
- InteractionJankMonitor.getInstance().begin(
+ mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("RunRemoteAnimation"));
runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps,
wallpapers, nonApps, callback);
@@ -2271,14 +2274,14 @@
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
mSurfaceBehindRemoteAnimationRunning = true;
- InteractionJankMonitor.getInstance().begin(
+ mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("DismissPanel"));
// Pass the surface and metadata to the unlock animation controller.
mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
} else {
- InteractionJankMonitor.getInstance().begin(
+ mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("RemoteAnimationDisabled"));
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
@@ -2288,7 +2291,7 @@
// supported, so it's always null.
mContext.getMainExecutor().execute(() -> {
if (finishedCallback == null) {
- InteractionJankMonitor.getInstance().end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
return;
}
@@ -2316,8 +2319,7 @@
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException");
} finally {
- InteractionJankMonitor.getInstance()
- .end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
}
@@ -2328,8 +2330,7 @@
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException");
} finally {
- InteractionJankMonitor.getInstance()
- .cancel(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index cae9fee..8d23e9f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager;
import android.os.PowerManager;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -97,7 +98,8 @@
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
- Lazy<NotificationShadeDepthController> notificationShadeDepthController) {
+ Lazy<NotificationShadeDepthController> notificationShadeDepthController,
+ InteractionJankMonitor interactionJankMonitor) {
return new KeyguardViewMediator(
context,
falsingCollector,
@@ -120,7 +122,8 @@
keyguardStateController,
keyguardUnlockAnimationController,
unlockedScreenOffAnimationController,
- notificationShadeDepthController
+ notificationShadeDepthController,
+ interactionJankMonitor
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index e87558e..d54b151 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -400,7 +400,7 @@
}
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
- mediaCarousel.requiresRemeasuring = true
+ mediaFrame.requiresRemeasuring = true
// Check postcondition: mediaContent should have the same number of children as there are
// elements in mediaPlayers.
if (MediaPlayerData.players().size != mediaContent.childCount) {
@@ -439,7 +439,7 @@
updatePlayerToState(newRecs, noAnimation = true)
reorderAllPlayers(curVisibleMediaKey)
updatePageIndicator()
- mediaCarousel.requiresRemeasuring = true
+ mediaFrame.requiresRemeasuring = true
// Check postcondition: mediaContent should have the same number of children as there are
// elements in mediaPlayers.
if (MediaPlayerData.players().size != mediaContent.childCount) {
@@ -833,12 +833,15 @@
)
private val comparator =
- compareByDescending<MediaSortKey> { it.data.isPlaying == true && it.data.isLocalSession }
- .thenByDescending { it.data.isPlaying }
- .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
- .thenByDescending { !it.data.resumption }
- .thenByDescending { it.updateTime }
- .thenByDescending { !it.data.isLocalSession }
+ compareByDescending<MediaSortKey> { it.data.isPlaying == true &&
+ it.data.playbackLocation == MediaData.PLAYBACK_LOCAL }
+ .thenByDescending { it.data.isPlaying == true &&
+ it.data.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL }
+ .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
+ .thenByDescending { !it.data.resumption }
+ .thenByDescending { it.data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE }
+ .thenByDescending { it.updateTime }
+ .thenByDescending { it.data.notificationKey }
private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator)
private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 6f0c887..bda07f4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -82,9 +82,9 @@
*/
var resumeAction: Runnable?,
/**
- * Local or remote playback
+ * Playback location: one of PLAYBACK_LOCAL, PLAYBACK_CAST_LOCAL, or PLAYBACK_CAST_REMOTE
*/
- var isLocalSession: Boolean = true,
+ var playbackLocation: Int = PLAYBACK_LOCAL,
/**
* Indicates that this player is a resumption player (ie. It only shows a play actions which
* will start the app and start playing).
@@ -110,7 +110,20 @@
* Timestamp when this player was last active.
*/
var lastActive: Long = 0L
-)
+) {
+ companion object {
+ /** Media is playing on the local device */
+ const val PLAYBACK_LOCAL = 0
+ /** Media is cast but originated on the local device */
+ const val PLAYBACK_CAST_LOCAL = 1
+ /** Media is from a remote cast notification */
+ const val PLAYBACK_CAST_REMOTE = 2
+ }
+
+ fun isLocalSession(): Boolean {
+ return playbackLocation == PLAYBACK_LOCAL
+ }
+}
/** State of a media action. */
data class MediaAction(
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index c9b6d9c..6e86bef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -27,6 +27,8 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.ImageDecoder
@@ -145,6 +147,24 @@
private var smartspaceSession: SmartspaceSession? = null
private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)
+ /**
+ * Check whether this notification is an RCN
+ * TODO(b/204910409) implement new API for explicitly declaring this
+ */
+ private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
+ val pm = context.packageManager
+ try {
+ val info = pm.getApplicationInfo(sbn.packageName, PackageManager.MATCH_SYSTEM_ONLY)
+ if (info.privateFlags and ApplicationInfo.PRIVATE_FLAG_PRIVILEGED != 0) {
+ val extras = sbn.notification.extras
+ if (extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
+ return true
+ }
+ }
+ } catch (e: PackageManager.NameNotFoundException) { }
+ return false
+ }
+
@Inject
constructor(
context: Context,
@@ -434,7 +454,7 @@
val existed = mediaEntries[key] != null
backgroundExecutor.execute {
mediaEntries[key]?.let { mediaData ->
- if (mediaData.isLocalSession) {
+ if (mediaData.isLocalSession()) {
mediaData.token?.let {
val mediaController = mediaControllerFactory.create(it)
mediaController.transportControls.stop()
@@ -618,8 +638,11 @@
}
}
- val isLocalSession = mediaController.playbackInfo?.playbackType ==
- MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL
+ val playbackLocation =
+ if (isRemoteCastNotification(sbn)) MediaData.PLAYBACK_CAST_REMOTE
+ else if (mediaController.playbackInfo?.playbackType ==
+ MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) MediaData.PLAYBACK_LOCAL
+ else MediaData.PLAYBACK_CAST_LOCAL
val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
val lastActive = systemClock.elapsedRealtime()
foregroundExecutor.execute {
@@ -629,7 +652,7 @@
onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
smallIcon, artist, song, artWorkIcon, actionIcons,
actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
- active, resumeAction = resumeAction, isLocalSession = isLocalSession,
+ active, resumeAction = resumeAction, playbackLocation = playbackLocation,
notificationKey = key, hasCheckedForResume = hasCheckedForResume,
isPlaying = isPlaying, isClearable = sbn.isClearable(),
lastActive = lastActive))
@@ -754,7 +777,7 @@
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
val removed = mediaEntries.remove(key)
- if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession) {
+ 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!!)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 608c784..d8095f3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -193,7 +193,7 @@
mediaBrowser = null
}
// If we don't have a resume action, check if we haven't already
- if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession) {
+ if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession()) {
// TODO also check for a media button receiver intended for restarting (b/154127084)
Log.d(TAG, "Checking for service component for " + data.packageName)
val pm = context.packageManager
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 57ac9df..237d077 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -16,11 +16,19 @@
package com.android.systemui.media.dagger;
+import android.content.Context;
+import android.view.WindowManager;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.media.MediaHostStatesManager;
+import com.android.systemui.media.taptotransfer.MediaTttChipController;
+import com.android.systemui.media.taptotransfer.MediaTttFlags;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
+
+import java.util.Optional;
import javax.inject.Named;
@@ -63,4 +71,18 @@
MediaHostStatesManager statesManager) {
return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
}
+
+ /** */
+ @Provides
+ @SysUISingleton
+ static Optional<MediaTttChipController> providesMediaTttChipController(
+ MediaTttFlags mediaTttFlags,
+ Context context,
+ CommandRegistry commandRegistry,
+ WindowManager windowManager) {
+ if (!mediaTttFlags.isMediaTttEnabled()) {
+ return Optional.empty();
+ }
+ return Optional.of(new MediaTttChipController(context, commandRegistry, windowManager));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 113ba59..5fa66cd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -40,6 +40,8 @@
private static final String TAG = "MediaOutputAdapter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final float DEVICE_DISCONNECTED_ALPHA = 0.5f;
+ private static final float DEVICE_CONNECTED_ALPHA = 1f;
private final MediaOutputDialog mMediaOutputDialog;
private ViewGroup mConnectedItem;
@@ -109,8 +111,8 @@
if (currentlyConnected) {
mConnectedItem = mContainerLayout;
}
- mBottomDivider.setVisibility(View.GONE);
mCheckBox.setVisibility(View.GONE);
+ mStatusIcon.setVisibility(View.GONE);
if (currentlyConnected && mController.isActiveRemoteDevice(device)
&& mController.getSelectableMediaDevice().size() > 0) {
// Init active device layout
@@ -124,35 +126,42 @@
if (mCurrentActivePosition == position) {
mCurrentActivePosition = -1;
}
+ if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
+ && !device.isConnected()) {
+ mTitleText.setAlpha(DEVICE_DISCONNECTED_ALPHA);
+ mTitleIcon.setAlpha(DEVICE_DISCONNECTED_ALPHA);
+ } else {
+ mTitleText.setAlpha(DEVICE_CONNECTED_ALPHA);
+ mTitleIcon.setAlpha(DEVICE_CONNECTED_ALPHA);
+ }
+
if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
- setTwoLineLayout(device, true /* bFocused */, false /* showSeekBar*/,
- true /* showProgressBar */, false /* showSubtitle */);
+ setSingleLineLayout(getItemTitle(device), true /* bFocused */,
+ false /* showSeekBar*/,
+ true /* showProgressBar */, false /* showStatus */);
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
}
} else {
// Set different layout for each device
if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
+ mStatusIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.media_output_status_failed));
setTwoLineLayout(device, false /* bFocused */,
false /* showSeekBar */, false /* showProgressBar */,
- true /* showSubtitle */);
+ true /* showSubtitle */, true /* showStatus */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
} else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
- setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */,
- false /* showProgressBar */, false /* showSubtitle */);
+ mStatusIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.media_output_status_check));
+ setSingleLineLayout(getItemTitle(device), true /* bFocused */,
+ true /* showSeekBar */,
+ false /* showProgressBar */, true /* showStatus */);
initSeekbar(device);
mCurrentActivePosition = position;
- } else if (
- device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
- && !device.isConnected()) {
- setTwoLineLayout(device, false /* bFocused */,
- false /* showSeekBar */, false /* showProgressBar */,
- true /* showSubtitle */);
- mSubTitleText.setText(R.string.media_output_dialog_disconnected);
- mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
@@ -165,7 +174,6 @@
if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
mCheckBox.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
- mBottomDivider.setVisibility(View.GONE);
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
false /* bFocused */);
final Drawable d = mContext.getDrawable(R.drawable.ic_add);
@@ -175,7 +183,6 @@
mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
} else if (customizedItem == CUSTOMIZED_ITEM_DYNAMIC_GROUP) {
mConnectedItem = mContainerLayout;
- mBottomDivider.setVisibility(View.GONE);
mCheckBox.setVisibility(View.GONE);
if (mController.getSelectableMediaDevice().size() > 0) {
mAddIcon.setVisibility(View.VISIBLE);
@@ -200,7 +207,6 @@
}
mCurrentActivePosition = -1;
- playSwitchingAnim(mConnectedItem, view);
mController.connectDevice(device);
device.setState(MediaDeviceState.STATE_CONNECTING);
if (!isAnimating()) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 868193b..dc4aaa9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -30,6 +30,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
@@ -40,7 +41,6 @@
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
@@ -113,6 +113,7 @@
private static final int ANIM_DURATION = 200;
final LinearLayout mContainerLayout;
+ final FrameLayout mItemLayout;
final TextView mTitleText;
final TextView mTwoLineTitleText;
final TextView mSubTitleText;
@@ -121,13 +122,14 @@
final ProgressBar mProgressBar;
final SeekBar mSeekBar;
final RelativeLayout mTwoLineLayout;
- final View mBottomDivider;
+ final ImageView mStatusIcon;
final CheckBox mCheckBox;
private String mDeviceId;
MediaDeviceBaseViewHolder(View view) {
super(view);
mContainerLayout = view.requireViewById(R.id.device_container);
+ mItemLayout = view.requireViewById(R.id.item_layout);
mTitleText = view.requireViewById(R.id.title);
mSubTitleText = view.requireViewById(R.id.subtitle);
mTwoLineLayout = view.requireViewById(R.id.two_line_layout);
@@ -135,8 +137,8 @@
mTitleIcon = view.requireViewById(R.id.title_icon);
mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
mSeekBar = view.requireViewById(R.id.volume_seekbar);
- mBottomDivider = view.requireViewById(R.id.bottom_divider);
mAddIcon = view.requireViewById(R.id.add_icon);
+ mStatusIcon = view.requireViewById(R.id.media_output_item_status);
mCheckBox = view.requireViewById(R.id.check_box);
}
@@ -156,11 +158,26 @@
abstract void onBind(int customizedItem, boolean topMargin, boolean bottomMargin);
void setSingleLineLayout(CharSequence title, boolean bFocused) {
+ setSingleLineLayout(title, bFocused, false, false, false);
+ }
+
+ void setSingleLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
+ boolean showProgressBar, boolean showStatus) {
mTwoLineLayout.setVisibility(View.GONE);
- mProgressBar.setVisibility(View.GONE);
+ final Drawable backgroundDrawable =
+ showSeekBar
+ ? mContext.getDrawable(R.drawable.media_output_item_background_active)
+ .mutate() : mContext.getDrawable(
+ R.drawable.media_output_item_background)
+ .mutate();
+ mItemLayout.setBackground(backgroundDrawable);
+ mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
+ mSeekBar.setAlpha(1);
+ mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
+ mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
+ mTitleText.setText(title);
mTitleText.setVisibility(View.VISIBLE);
mTitleText.setTranslationY(0);
- mTitleText.setText(title);
if (bFocused) {
mTitleText.setTypeface(Typeface.create(mContext.getString(
com.android.internal.R.string.config_headlineFontFamilyMedium),
@@ -173,20 +190,32 @@
void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
boolean showProgressBar, boolean showSubtitle) {
- setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle);
+ setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle,
+ false);
+ }
+
+ void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
+ boolean showProgressBar, boolean showSubtitle, boolean showStatus) {
+ setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle,
+ showStatus);
}
void setTwoLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
boolean showProgressBar, boolean showSubtitle) {
- setTwoLineLayout(null, title, bFocused, showSeekBar, showProgressBar, showSubtitle);
+ setTwoLineLayout(null, title, bFocused, showSeekBar, showProgressBar, showSubtitle,
+ false);
}
private void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
- boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
+ boolean showSeekBar, boolean showProgressBar, boolean showSubtitle,
+ boolean showStatus) {
mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE);
+ mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
mSeekBar.setAlpha(1);
mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
+ mItemLayout.setBackground(mContext.getDrawable(R.drawable.media_output_item_background)
+ .mutate());
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
mTwoLineTitleText.setTranslationY(0);
@@ -289,6 +318,9 @@
public void onAnimationEnd(Animator animation) {
to.requireViewById(R.id.volume_indeterminate_progress).setVisibility(
View.VISIBLE);
+ // Unset the listener, otherwise this may persist for another view
+ // property animation
+ toTitleText.animate().setListener(null);
}
});
// Animation for seek bar
@@ -312,8 +344,14 @@
public void onAnimationEnd(Animator animation) {
mIsAnimating = false;
notifyDataSetChanged();
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ fromTitleText.animate().setListener(null);
}
});
+ // Unset the listener, otherwise this may persist for another view
+ // property animation
+ fromSeekBar.animate().setListener(null);
}
});
}
@@ -325,7 +363,7 @@
R.color.advanced_icon_color, mContext.getTheme());
drawable.setColorFilter(new PorterDuffColorFilter(list.getDefaultColor(),
PorterDuff.Mode.SRC_IN));
- return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
+ return drawable;
}
private void disableSeekBar() {
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 26ce645..f7f9ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -20,6 +20,7 @@
import static android.view.WindowInsets.Type.statusBars;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -64,6 +65,7 @@
private TextView mHeaderTitle;
private TextView mHeaderSubtitle;
private ImageView mHeaderIcon;
+ private ImageView mAppResourceIcon;
private RecyclerView mDevicesRecyclerView;
private LinearLayout mDeviceListLayout;
private Button mDoneButton;
@@ -112,6 +114,7 @@
mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
mDoneButton = mDialogView.requireViewById(R.id.done);
mStopButton = mDialogView.requireViewById(R.id.stop);
+ mAppResourceIcon = mDialogView.requireViewById(R.id.app_source_icon);
mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
mDeviceListLayoutListener);
@@ -145,6 +148,12 @@
// Update header icon
final int iconRes = getHeaderIconRes();
final IconCompat iconCompat = getHeaderIcon();
+ final Drawable appSourceDrawable = getAppSourceIcon();
+ if (appSourceDrawable != null) {
+ mAppResourceIcon.setImageDrawable(appSourceDrawable);
+ } else {
+ mAppResourceIcon.setVisibility(View.GONE);
+ }
if (iconRes != 0) {
mHeaderIcon.setVisibility(View.VISIBLE);
mHeaderIcon.setImageResource(iconRes);
@@ -183,6 +192,8 @@
mStopButton.setVisibility(getStopButtonVisibility());
}
+ abstract Drawable getAppSourceIcon();
+
abstract int getHeaderIconRes();
abstract IconCompat getHeaderIcon();
@@ -217,14 +228,6 @@
dismiss();
}
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- if (!hasFocus && isShowing()) {
- dismiss();
- }
- }
-
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 6da4d48..eef5fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -19,6 +19,7 @@
import android.app.Notification;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -181,6 +182,20 @@
mMetricLogger.logOutputFailure(mMediaDevices, reason);
}
+ Drawable getAppSourceIcon() {
+ if (mPackageName.isEmpty()) {
+ return null;
+ }
+ try {
+ Log.d(TAG, "try to get app icon");
+ return mContext.getPackageManager()
+ .getApplicationIcon(mPackageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d(TAG, "icon not found");
+ return null;
+ }
+ }
+
CharSequence getHeaderTitle() {
if (mMediaController != null) {
final MediaMetadata metadata = mMediaController.getMetadata();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index eca8ac9..7696a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -17,6 +17,7 @@
package com.android.systemui.media.dialog;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
@@ -79,6 +80,11 @@
}
@Override
+ Drawable getAppSourceIcon() {
+ return mMediaOutputController.getAppSourceIcon();
+ }
+
+ @Override
int getStopButtonVisibility() {
return mMediaOutputController.isActiveRemoteDevice(
mMediaOutputController.getCurrentConnectedMediaDevice()) ? View.VISIBLE : View.GONE;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
index a201c07..104ddf9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -97,7 +97,6 @@
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
super.onBind(device, topMargin, bottomMargin, position);
mAddIcon.setVisibility(View.GONE);
- mBottomDivider.setVisibility(View.GONE);
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
onCheckBoxClicked(isChecked, device);
@@ -131,7 +130,6 @@
true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */,
false /* showSubtitle*/);
mTitleIcon.setImageDrawable(getSpeakerDrawable());
- mBottomDivider.setVisibility(View.VISIBLE);
mCheckBox.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
initSessionSeekbar();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index 1300400..f1c6601 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -17,6 +17,7 @@
package com.android.systemui.media.dialog;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
@@ -28,6 +29,7 @@
/**
* Dialog for media output group.
*/
+// TODO(b/203073091): Remove this class once group logic been implemented.
public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
@@ -76,6 +78,11 @@
}
@Override
+ Drawable getAppSourceIcon() {
+ return null;
+ }
+
+ @Override
int getStopButtonVisibility() {
return View.VISIBLE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
new file mode 100644
index 0000000..85e5b33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.media.taptotransfer
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.view.Gravity
+import android.view.WindowManager
+import android.widget.TextView
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * A controller to display and hide the Media Tap-To-Transfer chip. This chip is shown when a user
+ * is currently playing media on a local "media cast sender" device (e.g. a phone) and gets close
+ * enough to a "media cast receiver" device (e.g. a tablet). This chip encourages the user to
+ * transfer the media from the sender device to the receiver device.
+ */
+@SysUISingleton
+class MediaTttChipController @Inject constructor(
+ context: Context,
+ commandRegistry: CommandRegistry,
+ private val windowManager: WindowManager,
+) {
+ init {
+ commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
+ commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() }
+ }
+
+ private val windowLayoutParams = WindowManager.LayoutParams().apply {
+ width = WindowManager.LayoutParams.MATCH_PARENT
+ height = WindowManager.LayoutParams.MATCH_PARENT
+ gravity = Gravity.CENTER_HORIZONTAL
+ type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
+ title = "Media Tap-To-Transfer Chip View"
+ flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+ format = PixelFormat.TRANSLUCENT
+ setTrustedOverlay()
+ }
+
+ // TODO(b/203800327): Create a layout that matches UX.
+ private val chipView: TextView = TextView(context).apply {
+ text = "Media Tap-To-Transfer Chip"
+ }
+
+ private var chipDisplaying: Boolean = false
+
+ private fun addChip() {
+ if (chipDisplaying) { return }
+ windowManager.addView(chipView, windowLayoutParams)
+ chipDisplaying = true
+ }
+
+ private fun removeChip() {
+ if (!chipDisplaying) { return }
+ windowManager.removeView(chipView)
+ chipDisplaying = false
+ }
+
+ inner class AddChipCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) = addChip()
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG")
+ }
+ }
+
+ inner class RemoveChipCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) = removeChip()
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG")
+ }
+ }
+
+ companion object {
+ @VisibleForTesting
+ const val ADD_CHIP_COMMAND_TAG = "media-ttt-chip-add"
+ @VisibleForTesting
+ const val REMOVE_CHIP_COMMAND_TAG = "media-ttt-chip-remove"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
new file mode 100644
index 0000000..03bc935
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.media.taptotransfer
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/** Flags related to media tap-to-transfer. */
+@SysUISingleton
+class MediaTttFlags @Inject constructor(private val featureFlags: FeatureFlags) {
+ /** */
+ fun isMediaTttEnabled(): Boolean = featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
new file mode 100644
index 0000000..25337b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -0,0 +1,272 @@
+/*
+ * 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.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.inputmethodservice.InputMethodService;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/**
+ * Extracts shared elements between navbar and taskbar delegate to de-dupe logic and help them
+ * experience the joys of friendship.
+ * The events are then passed through
+ *
+ * Currently consolidates
+ * * A11y
+ * * Assistant
+ */
+@SysUISingleton
+public final class NavBarHelper implements
+ AccessibilityButtonModeObserver.ModeChangedListener,
+ OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ Dumpable {
+ private final AccessibilityManager mAccessibilityManager;
+ private final Lazy<AssistManager> mAssistManagerLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final UserTracker mUserTracker;
+ private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
+ private final Context mContext;
+ private ContentResolver mContentResolver;
+ private boolean mAssistantAvailable;
+ private boolean mLongPressHomeEnabled;
+ private boolean mAssistantTouchGestureEnabled;
+ private int mNavBarMode;
+
+ private final ContentObserver mAssistContentObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateAssitantAvailability();
+ }
+ };
+
+ /**
+ * @param context This is not display specific, then again neither is any of the code in
+ * this class. Once there's display specific code, we may want to create an
+ * instance of this class per navbar vs having it be a singleton.
+ */
+ @Inject
+ public NavBarHelper(Context context, AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+ OverviewProxyService overviewProxyService,
+ Lazy<AssistManager> assistManagerLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ NavigationModeController navigationModeController,
+ UserTracker userTracker,
+ DumpManager dumpManager) {
+ mContext = context;
+ mAccessibilityManager = accessibilityManager;
+ mAssistManagerLazy = assistManagerLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mUserTracker = userTracker;
+ accessibilityManagerWrapper.addCallback(
+ accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate());
+ mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+
+ mAccessibilityButtonModeObserver.addListener(this);
+ mNavBarMode = navigationModeController.addListener(this);
+ overviewProxyService.addCallback(this);
+ dumpManager.registerDumpable(this);
+ }
+
+ public void init() {
+ mContentResolver = mContext.getContentResolver();
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
+ false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ updateAssitantAvailability();
+ }
+
+ public void destroy() {
+ mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ }
+
+ /**
+ * @param listener Will immediately get callbacks based on current state
+ */
+ public void registerNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.add(listener);
+ listener.updateAccessibilityServicesState();
+ listener.updateAssistantAvailable(mAssistantAvailable);
+ }
+
+ public void removeNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.remove(listener);
+ }
+
+ private void dispatchA11yEventUpdate() {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAccessibilityServicesState();
+ }
+ }
+
+ private void dispatchAssistantEventUpdate(boolean assistantAvailable) {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAssistantAvailable(assistantAvailable);
+ }
+ }
+
+ @Override
+ public void onAccessibilityButtonModeChanged(int mode) {
+ dispatchA11yEventUpdate();
+ }
+
+ /**
+ * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ *
+ * @return the a11y button clickable and long_clickable states, or 0 if there is no
+ * a11y button in the navbar
+ */
+ public int getA11yButtonState() {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return 0;
+ }
+
+ return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateAssitantAvailability();
+ }
+ }
+
+ private void updateAssitantAvailability() {
+ boolean assistantAvailableForUser = mAssistManagerLazy.get()
+ .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ boolean longPressDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
+ mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+ boolean gestureDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
+ mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+
+ mAssistantAvailable = assistantAvailableForUser
+ && mAssistantTouchGestureEnabled
+ && QuickStepContract.isGesturalMode(mNavBarMode);
+ dispatchAssistantEventUpdate(mAssistantAvailable);
+ }
+
+ public boolean getLongPressHomeEnabled() {
+ return mLongPressHomeEnabled;
+ }
+
+ @Override
+ public void startAssistant(Bundle bundle) {
+ mAssistManagerLazy.get().startAssist(bundle);
+ }
+
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ updateAssitantAvailability();
+ }
+
+ /**
+ * @return Whether the IME is shown on top of the screen given the {@code vis} flag of
+ * {@link InputMethodService} and the keyguard states.
+ */
+ public boolean isImeShown(int vis) {
+ View shadeWindowView = mStatusBarOptionalLazy.get().get().getNotificationShadeWindowView();
+ boolean isKeyguardShowing = mStatusBarOptionalLazy.get().get().isKeyguardShowing();
+ boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
+ && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+ return imeVisibleOnShade
+ || (!isKeyguardShowing && (vis & InputMethodService.IME_VISIBLE) != 0);
+ }
+
+ /**
+ * Callbacks will get fired once immediately after registering via
+ * {@link #registerNavTaskStateUpdater(NavbarTaskbarStateUpdater)}
+ */
+ public interface NavbarTaskbarStateUpdater {
+ void updateAccessibilityServicesState();
+ void updateAssistantAvailable(boolean available);
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("NavbarTaskbarFriendster");
+ pw.println(" longPressHomeEnabled=" + mLongPressHomeEnabled);
+ pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
+ pw.println(" mAssistantAvailable=" + mAssistantAvailable);
+ pw.println(" mNavBarMode=" + mNavBarMode);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index bdacc41..8026df7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -67,22 +67,17 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.database.ContentObserver;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.inputmethodservice.InputMethodService;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
@@ -97,7 +92,6 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
@@ -200,8 +194,7 @@
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
- private final UserTracker mUserTracker;
+ private final NavBarHelper mNavBarHelper;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private Bundle mSavedState;
@@ -213,9 +206,7 @@
private int mNavigationIconHints = 0;
private @TransitionMode int mNavigationBarMode;
private ContentResolver mContentResolver;
- private boolean mAssistantAvailable;
private boolean mLongPressHomeEnabled;
- private boolean mAssistantTouchGestureEnabled;
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -309,16 +300,31 @@
}
};
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateAcessibilityStateFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ // TODO(b/198002034): Content observers currently can still be called back after
+ // being unregistered, and in this case we can ignore the change if the nav bar
+ // has been destroyed already
+ if (mNavigationBarView == null) {
+ return;
+ }
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
+ updateAssistantEntrypoints(available);
+ }
+ };
+
private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
mNavigationBarView.updateStates();
updateScreenPinningGestures();
-
- // Send the assistant availability upon connection
- if (isConnected) {
- updateAssistantEntrypoints();
- }
}
@Override
@@ -421,20 +427,6 @@
}
};
- private final ContentObserver mAssistContentObserver = new ContentObserver(
- new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- // TODO(b/198002034): Content observers currently can still be called back after being
- // unregistered, and in this case we can ignore the change if the nav bar has been
- // destroyed already
- if (mNavigationBarView == null) {
- return;
- }
- updateAssistantEntrypoints();
- }
- };
-
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -504,7 +496,7 @@
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -535,8 +527,7 @@
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
- mUserTracker = userTracker;
+ mNavBarHelper = navBarHelper;
mNotificationShadeDepthController = notificationShadeDepthController;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -568,18 +559,9 @@
mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mCommandQueue.addCallback(this);
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mContentResolver = mContext.getContentResolver();
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
- false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
+ mNavBarHelper.init();
mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
R.bool.allow_force_nav_bar_handle_opaque);
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
@@ -593,7 +575,6 @@
)).filter(duration -> duration != 0);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
- updateAssistantEntrypoints();
if (savedState != null) {
mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -620,8 +601,8 @@
mWindowManager.removeViewImmediate(mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
- mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
@@ -643,7 +624,7 @@
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -716,7 +697,7 @@
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
@@ -885,7 +866,6 @@
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
- pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
pw.println(" mNavigationBarWindowState="
+ windowStateToString(mNavigationBarWindowState));
pw.println(" mNavigationBarMode="
@@ -902,17 +882,8 @@
if (displayId != mDisplayId) {
return;
}
- boolean imeVisibleOnShade = mStatusBarOptionalLazy.get().map(statusBar -> {
- View shadeWindowView = statusBar.getNotificationShadeWindowView();
- return shadeWindowView.isAttachedToWindow()
- && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
- }).orElse(false);
- boolean isKeyguardShowing = mStatusBarOptionalLazy.get().map(
- StatusBar::isKeyguardShowing).orElse(false);
- boolean imeShown = imeVisibleOnShade
- || (!isKeyguardShowing && (vis & InputMethodService.IME_VISIBLE) != 0);
+ boolean imeShown = mNavBarHelper.isImeShown(vis);
showImeSwitcher = imeShown && showImeSwitcher;
-
int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
imeShown, showImeSwitcher);
if (hints == mNavigationIconHints) return;
@@ -1175,7 +1146,7 @@
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1400,8 +1371,8 @@
return true;
}
- void updateAccessibilityServicesState() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ void updateAcessibilityStateFlags() {
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
if (mNavigationBarView != null) {
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
@@ -1413,7 +1384,7 @@
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
- a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ a11yFlags = mNavBarHelper.getA11yButtonState();
}
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1440,24 +1411,10 @@
}
}
- private void updateAssistantEntrypoints() {
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
- boolean longPressDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
- mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
- boolean gestureDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
- mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
+ private void updateAssistantEntrypoints(boolean assistantAvailable) {
if (mOverviewProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
- && mAssistantTouchGestureEnabled
- && QuickStepContract.isGesturalMode(mNavBarMode));
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
}
@@ -1528,8 +1485,6 @@
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
- // update assistant entry points on system navigation radio button click
- updateAssistantEntrypoints();
if (!QuickStepContract.isGesturalMode(mode)) {
// Reset the override alpha
@@ -1568,9 +1523,6 @@
mNavigationBarView.getBarTransitions().finishAnimations();
}
- private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
- this::updateAccessibilityServicesState;
-
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
lp.paramsForRotation = new WindowManager.LayoutParams[4];
@@ -1674,7 +1626,7 @@
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
}
}
};
@@ -1710,7 +1662,7 @@
private final Handler mMainHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private final NavBarHelper mNavBarHelper;
private final UserTracker mUserTracker;
private final LightBarController mMainLightBarController;
private final LightBarController.Factory mLightBarControllerFactory;
@@ -1743,7 +1695,7 @@
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -1773,7 +1725,7 @@
mMainHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mUserTracker = userTracker;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -1794,7 +1746,7 @@
mSplitScreenOptional, mRecentsOptional, mStatusBarOptionalLazy,
mShadeController, mNotificationRemoteInputManager,
mNotificationShadeDepthController, mSystemActions, mMainHandler,
- mNavbarOverlayController, mUiEventLogger, mNavigationBarA11yHelper,
+ mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
mUserTracker, mMainLightBarController, mLightBarControllerFactory,
mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
mInputMethodManager);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
deleted file mode 100644
index 13e6d8b..0000000
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.android.systemui.navigationbar;
-
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
-
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Extracts shared elements of a11y necessary between navbar and taskbar delegate
- */
-@SysUISingleton
-public final class NavigationBarA11yHelper implements
- AccessibilityButtonModeObserver.ModeChangedListener {
- private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
- private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
-
- @Inject
- public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
- mAccessibilityManager = accessibilityManager;
- accessibilityManagerWrapper.addCallback(
- accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
- mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
-
- mAccessibilityButtonModeObserver.addListener(this);
- }
-
- public void registerA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.add(listener);
- }
-
- public void removeA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.remove(listener);
- }
-
- private void dispatchEventUpdate() {
- for (NavA11yEventListener listener : mA11yEventListeners) {
- listener.updateAccessibilityServicesState();
- }
- }
-
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- dispatchEventUpdate();
- }
-
- /**
- * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
- * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
- *
- * @return the a11y button clickable and long_clickable states, or 0 if there is no
- * a11y button in the navbar
- */
- public int getA11yButtonState() {
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
-
- public interface NavA11yEventListener {
- void updateAccessibilityServicesState();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 3dc79c4..bfabf71 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,6 +57,7 @@
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.FileDescriptor;
@@ -100,11 +101,12 @@
CommandQueue commandQueue,
@Main Handler mainHandler,
ConfigurationController configurationController,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
TaskbarDelegate taskbarDelegate,
NavigationBar.Factory navigationBarFactory,
DumpManager dumpManager,
- AutoHideController autoHideController) {
+ AutoHideController autoHideController,
+ LightBarController lightBarController) {
mContext = context;
mHandler = mainHandler;
mNavigationBarFactory = navigationBarFactory;
@@ -115,8 +117,8 @@
mNavMode = navigationModeController.addListener(this);
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
- navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer,
- dumpManager, autoHideController);
+ navBarHelper, navigationModeController, sysUiFlagsContainer,
+ dumpManager, autoHideController, lightBarController);
mIsTablet = isTablet(mContext);
dumpManager.registerDumpable(this);
}
@@ -393,6 +395,24 @@
return (navBar == null) ? null : navBar.getView();
}
+ public void showPinningEnterExitToast(int displayId, boolean entering) {
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ navBarView.showPinningEnterExitToast(entering);
+ } else if (displayId == DEFAULT_DISPLAY && mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.showPinningEnterExitToast(entering);
+ }
+ }
+
+ public void showPinningEscapeToast(int displayId) {
+ final NavigationBarView navBarView = getNavigationBarView(displayId);
+ if (navBarView != null) {
+ navBarView.showPinningEscapeToast();
+ } else if (displayId == DEFAULT_DISPLAY && mTaskbarDelegate.isInitialized()) {
+ mTaskbarDelegate.showPinningEscapeToast();
+ }
+ }
+
/** @return {@link NavigationBar} on the default display. */
@Nullable
public NavigationBar getDefaultNavigationBar() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 680cc5c..7c8c3e0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -110,6 +110,7 @@
private final int mNavColorSampleMargin;
private final SysUiState mSysUiFlagContainer;
+ // The current view is one of mHorizontal or mVertical depending on the current configuration
View mCurrentView = null;
private View mVertical;
private View mHorizontal;
@@ -397,12 +398,6 @@
}
}
- @Override
- protected boolean onSetAlpha(int alpha) {
- Log.e(TAG, "onSetAlpha", new Throwable());
- return super.onSetAlpha(alpha);
- }
-
public void setAutoHideController(AutoHideController autoHideController) {
mAutoHideController = autoHideController;
}
@@ -505,6 +500,18 @@
return mCurrentView;
}
+ /**
+ * Applies {@param consumer} to each of the nav bar views.
+ */
+ public void forEachView(Consumer<View> consumer) {
+ if (mVertical != null) {
+ consumer.accept(mVertical);
+ }
+ if (mHorizontal != null) {
+ consumer.accept(mHorizontal);
+ }
+ }
+
public RotationButtonController getRotationButtonController() {
return mRotationButtonController;
}
@@ -1205,7 +1212,9 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mTmpLastConfiguration.updateFrom(mConfiguration);
- mConfiguration.updateFrom(newConfig);
+ final int changes = mConfiguration.updateFrom(newConfig);
+ mFloatingRotationButton.onConfigurationChanged(changes);
+
boolean uiCarModeChanged = updateCarMode();
updateIcons(mTmpLastConfiguration);
updateRecentsIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index d707dbd..68f4aea 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -43,6 +43,8 @@
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.Display;
import android.view.InsetsVisibilities;
import android.view.View;
@@ -59,9 +61,13 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -73,25 +79,41 @@
public class TaskbarDelegate implements CommandQueue.Callbacks,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
ComponentCallbacks, Dumpable {
+ private static final String TAG = TaskbarDelegate.class.getSimpleName();
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
-
+ private boolean mInitialized;
private CommandQueue mCommandQueue;
private OverviewProxyService mOverviewProxyService;
- private NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private NavBarHelper mNavBarHelper;
private NavigationModeController mNavigationModeController;
private SysUiState mSysUiState;
private AutoHideController mAutoHideController;
+ private LightBarController mLightBarController;
+ private LightBarTransitionsController mLightBarTransitionsController;
private int mDisplayId;
private int mNavigationIconHints;
- private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
- this::updateSysuiFlags;
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateSysuiFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ updateAssistantAvailability(available);
+ }
+ };
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
private @Behavior int mBehavior;
private final Context mContext;
private final DisplayManager mDisplayManager;
private Context mWindowContext;
+ private ScreenPinningNotify mScreenPinningNotify;
+ private int mNavigationMode;
+
/**
* Tracks the system calls for when taskbar should transiently show or hide so we can return
* this value in {@link AutoHideUiElement#isVisible()} below.
@@ -125,52 +147,100 @@
public void setDependencies(CommandQueue commandQueue,
OverviewProxyService overviewProxyService,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
NavigationModeController navigationModeController,
SysUiState sysUiState, DumpManager dumpManager,
- AutoHideController autoHideController) {
+ AutoHideController autoHideController,
+ LightBarController lightBarController) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mNavigationModeController = navigationModeController;
mSysUiState = sysUiState;
dumpManager.registerDumpable(this);
mAutoHideController = autoHideController;
+ mLightBarController = lightBarController;
+ mLightBarTransitionsController = createLightBarTransitionsController();
+ }
+
+ // Separated into a method to keep setDependencies() clean/readable.
+ private LightBarTransitionsController createLightBarTransitionsController() {
+ return new LightBarTransitionsController(mContext,
+ new LightBarTransitionsController.DarkIntensityApplier() {
+ @Override
+ public void applyDarkIntensity(float darkIntensity) {
+ mOverviewProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
+ }
+
+ @Override
+ public int getTintAnimationDuration() {
+ return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
+ }
+ }, mCommandQueue) {
+ @Override
+ public boolean supportsIconTintForNavMode(int navigationMode) {
+ // Always tint taskbar nav buttons (region sampling handles gesture bar separately).
+ return true;
+ }
+ };
}
public void init(int displayId) {
+ if (mInitialized) {
+ return;
+ }
mDisplayId = displayId;
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(this);
mEdgeBackGestureHandler.onNavigationModeChanged(
mNavigationModeController.addListener(this));
- mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.init();
mEdgeBackGestureHandler.onNavBarAttached();
// Initialize component callback
Display display = mDisplayManager.getDisplay(displayId);
mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
mWindowContext.registerComponentCallbacks(this);
+ mScreenPinningNotify = new ScreenPinningNotify(mWindowContext);
// Set initial state for any listeners
updateSysuiFlags();
mAutoHideController.setNavigationBar(mAutoHideUiElement);
+ mLightBarController.setNavigationBar(mLightBarTransitionsController);
+ mInitialized = true;
}
public void destroy() {
+ if (!mInitialized) {
+ return;
+ }
mCommandQueue.removeCallback(this);
mOverviewProxyService.removeCallback(this);
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mEdgeBackGestureHandler.onNavBarDetached();
+ mScreenPinningNotify = null;
if (mWindowContext != null) {
mWindowContext.unregisterComponentCallbacks(this);
mWindowContext = null;
}
mAutoHideController.setNavigationBar(null);
+ mLightBarTransitionsController.destroy(mContext);
+ mLightBarController.setNavigationBar(null);
+ mInitialized = false;
+ }
+
+ /**
+ * Returns {@code true} if this taskBar is {@link #init(int)}. Returns {@code false} if this
+ * taskbar has not yet been {@link #init(int)} or has been {@link #destroy()}.
+ */
+ public boolean isInitialized() {
+ return mInitialized;
}
private void updateSysuiFlags() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -194,10 +264,27 @@
.commitUpdate(mDisplayId);
}
+ private void updateAssistantAvailability(boolean assistantAvailable) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onAssistantAvailable() failed, available: " + assistantAvailable, e);
+ }
+ }
+
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
- boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+ boolean imeShown = mNavBarHelper.isImeShown(vis);
+ if (!imeShown) {
+ // Count imperceptible changes as visible so we transition taskbar out quickly.
+ imeShown = (vis & InputMethodService.IME_VISIBLE_IMPERCEPTIBLE) != 0;
+ }
+ showImeSwitcher = imeShown && showImeSwitcher;
int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
imeShown, showImeSwitcher);
if (hints != mNavigationIconHints) {
@@ -233,6 +320,10 @@
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
InsetsVisibilities requestedVisibilities, String packageName) {
mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
+ if (mLightBarController != null && displayId == mDisplayId) {
+ mLightBarController.onNavigationBarAppearanceChanged(appearance, false/*nbModeChanged*/,
+ BarTransitions.MODE_TRANSPARENT /*navigationBarMode*/, navbarColorManagedByIme);
+ }
if (mBehavior != behavior) {
mBehavior = behavior;
updateSysuiFlags();
@@ -273,6 +364,7 @@
@Override
public void onNavigationModeChanged(int mode) {
+ mNavigationMode = mode;
mEdgeBackGestureHandler.onNavigationModeChanged(mode);
}
@@ -293,9 +385,33 @@
public void onLowMemory() {}
@Override
+ public void showPinningEnterExitToast(boolean entering) {
+ updateSysuiFlags();
+ if (mScreenPinningNotify == null) {
+ return;
+ }
+ if (entering) {
+ mScreenPinningNotify.showPinningStartToast();
+ } else {
+ mScreenPinningNotify.showPinningExitToast();
+ }
+ }
+
+ @Override
+ public void showPinningEscapeToast() {
+ updateSysuiFlags();
+ if (mScreenPinningNotify == null) {
+ return;
+ }
+ mScreenPinningNotify.showEscapeToast(QuickStepContract.isGesturalMode(mNavigationMode),
+ !QuickStepContract.isGesturalMode(mNavigationMode));
+ }
+
+ @Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("TaskbarDelegate (displayId=" + mDisplayId + "):");
pw.println(" mNavigationIconHints=" + mNavigationIconHints);
+ pw.println(" mNavigationMode=" + mNavigationMode);
pw.println(" mDisabledFlags=" + mDisabledFlags);
pw.println(" mTaskBarWindowState=" + mTaskBarWindowState);
pw.println(" mBehavior=" + mBehavior);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 98b9146..e10e4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -17,8 +17,10 @@
package com.android.systemui.qs
import android.content.Intent
+import android.os.Handler
import android.os.UserManager
import android.provider.Settings
+import android.provider.Settings.Global.USER_SWITCHER_ENABLED
import android.view.View
import android.widget.Toast
import androidx.annotation.VisibleForTesting
@@ -35,6 +37,7 @@
import com.android.systemui.qs.FooterActionsController.ExpansionState.COLLAPSED
import com.android.systemui.qs.FooterActionsController.ExpansionState.EXPANDED
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.phone.SettingsButton
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -42,6 +45,7 @@
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.ViewController
+import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import javax.inject.Named
@@ -55,6 +59,7 @@
private val qsPanelController: QSPanelController,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
+ private val userTracker: UserTracker,
private val userInfoController: UserInfoController,
private val multiUserSwitchController: MultiUserSwitchController,
private val deviceProvisionedController: DeviceProvisionedController,
@@ -64,7 +69,9 @@
private val globalActionsDialog: GlobalActionsDialogLite,
private val uiEventLogger: UiEventLogger,
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
- private val buttonsVisibleState: ExpansionState
+ private val buttonsVisibleState: ExpansionState,
+ private val globalSetting: GlobalSettings,
+ private val handler: Handler
) : ViewController<FooterActionsView>(view) {
enum class ExpansionState { COLLAPSED, EXPANDED }
@@ -83,6 +90,16 @@
mView.onUserInfoChanged(picture, isGuestUser)
}
+ private val multiUserSetting =
+ object : SettingObserver(
+ globalSetting, handler, USER_SWITCHER_ENABLED, userTracker.userId) {
+ override fun handleValueChanged(value: Int, observedChange: Boolean) {
+ if (observedChange) {
+ updateView()
+ }
+ }
+ }
+
private val onClickListener = View.OnClickListener { v ->
// Don't do anything until views are unhidden. Don't do anything if the tap looks
// suspicious.
@@ -116,7 +133,7 @@
}
} else if (v === powerMenuLite) {
uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
- globalActionsDialog.showOrHideDialog(false, true)
+ globalActionsDialog.showOrHideDialog(false, true, v)
}
}
@@ -182,6 +199,7 @@
return
}
this.listening = listening
+ multiUserSetting.isListening = listening
if (this.listening) {
userInfoController.addCallback(onUserInfoChangedListener)
updateView()
@@ -215,4 +233,4 @@
}
private fun isTunerEnabled() = tunerService.isTunerEnabled
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
index f6c89a9..dd4dc87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
@@ -16,18 +16,22 @@
package com.android.systemui.qs
+import android.os.Handler
import android.os.UserManager
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.FooterActionsController.ExpansionState
import com.android.systemui.qs.dagger.QSFlagsModule
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import javax.inject.Named
@@ -35,6 +39,7 @@
private val qsPanelController: QSPanelController,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
+ private val userTracker: UserTracker,
private val userInfoController: UserInfoController,
private val multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
private val deviceProvisionedController: DeviceProvisionedController,
@@ -43,7 +48,9 @@
private val tunerService: TunerService,
private val globalActionsDialog: GlobalActionsDialogLite,
private val uiEventLogger: UiEventLogger,
- @Named(QSFlagsModule.PM_LITE_ENABLED) private val showPMLiteButton: Boolean
+ @Named(QSFlagsModule.PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
+ private val globalSettings: GlobalSettings,
+ @Main private val handler: Handler
) {
private lateinit var view: FooterActionsView
private lateinit var buttonsVisibleState: ExpansionState
@@ -60,8 +67,9 @@
fun build(): FooterActionsController {
return FooterActionsController(view, qsPanelController, activityStarter, userManager,
- userInfoController, multiUserSwitchControllerFactory.create(view),
+ userTracker, userInfoController, multiUserSwitchControllerFactory.create(view),
deviceProvisionedController, falsingManager, metricsLogger, tunerService,
- globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState)
+ globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState,
+ globalSettings, handler)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
new file mode 100644
index 0000000..3b305bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -0,0 +1,146 @@
+package com.android.systemui.qs
+
+import android.view.View
+import com.android.internal.R
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyChipEvent
+import com.android.systemui.privacy.PrivacyDialogController
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import javax.inject.Inject
+
+interface ChipVisibilityListener {
+ fun onChipVisibilityRefreshed(visible: Boolean)
+}
+
+/**
+ * Controls privacy icons/chip residing in QS header which show up when app is using camera,
+ * microphone or location.
+ * Manages their visibility depending on privacy signals coming from [PrivacyItemController].
+ *
+ * Unlike typical controller extending [com.android.systemui.util.ViewController] this view doesn't
+ * observe its attachment state because depending on where it is used, it might be never detached.
+ * Instead, parent controller should use [onParentVisible] and [onParentInvisible] to "activate" or
+ * "deactivate" this controller.
+ */
+class HeaderPrivacyIconsController @Inject constructor(
+ private val privacyItemController: PrivacyItemController,
+ private val uiEventLogger: UiEventLogger,
+ private val privacyChip: OngoingPrivacyChip,
+ private val privacyDialogController: PrivacyDialogController,
+ private val privacyLogger: PrivacyLogger,
+ private val iconContainer: StatusIconContainer
+) {
+
+ var chipVisibilityListener: ChipVisibilityListener? = null
+ private var listening = false
+ private var micCameraIndicatorsEnabled = false
+ private var locationIndicatorsEnabled = false
+ private var privacyChipLogged = false
+ private val cameraSlot = privacyChip.resources.getString(R.string.status_bar_camera)
+ private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
+ private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
+
+ private val picCallback: PrivacyItemController.Callback =
+ object : PrivacyItemController.Callback {
+ override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ privacyChip.privacyList = privacyItems
+ setChipVisibility(privacyItems.isNotEmpty())
+ }
+
+ override fun onFlagMicCameraChanged(flag: Boolean) {
+ if (micCameraIndicatorsEnabled != flag) {
+ micCameraIndicatorsEnabled = flag
+ update()
+ }
+ }
+
+ override fun onFlagLocationChanged(flag: Boolean) {
+ if (locationIndicatorsEnabled != flag) {
+ locationIndicatorsEnabled = flag
+ update()
+ }
+ }
+
+ private fun update() {
+ updatePrivacyIconSlots()
+ setChipVisibility(privacyChip.privacyList.isNotEmpty())
+ }
+ }
+
+ private fun getChipEnabled() = micCameraIndicatorsEnabled || locationIndicatorsEnabled
+
+ fun onParentVisible() {
+ privacyChip.setOnClickListener {
+ // If the privacy chip is visible, it means there were some indicators
+ uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
+ privacyDialogController.showDialog(privacyChip.context)
+ }
+ setChipVisibility(privacyChip.visibility == View.VISIBLE)
+ micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
+ locationIndicatorsEnabled = privacyItemController.locationAvailable
+
+ // Ignore privacy icons because they show in the space above QQS
+ updatePrivacyIconSlots()
+ }
+
+ fun onParentInvisible() {
+ chipVisibilityListener = null
+ privacyChip.setOnClickListener(null)
+ }
+
+ fun startListening() {
+ listening = true
+ // Get the most up to date info
+ micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
+ locationIndicatorsEnabled = privacyItemController.locationAvailable
+ privacyItemController.addCallback(picCallback)
+ }
+
+ fun stopListening() {
+ listening = false
+ privacyItemController.removeCallback(picCallback)
+ privacyChipLogged = false
+ }
+
+ private fun setChipVisibility(visible: Boolean) {
+ if (visible && getChipEnabled()) {
+ privacyLogger.logChipVisible(true)
+ // Makes sure that the chip is logged as viewed at most once each time QS is opened
+ // mListening makes sure that the callback didn't return after the user closed QS
+ if (!privacyChipLogged && listening) {
+ privacyChipLogged = true
+ uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW)
+ }
+ } else {
+ privacyLogger.logChipVisible(false)
+ }
+
+ privacyChip.visibility = if (visible) View.VISIBLE else View.GONE
+ chipVisibilityListener?.onChipVisibilityRefreshed(visible)
+ }
+
+ private fun updatePrivacyIconSlots() {
+ if (getChipEnabled()) {
+ if (micCameraIndicatorsEnabled) {
+ iconContainer.addIgnoredSlot(cameraSlot)
+ iconContainer.addIgnoredSlot(micSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ }
+ if (locationIndicatorsEnabled) {
+ iconContainer.addIgnoredSlot(locationSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
index 2f189be..768598a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -133,10 +133,7 @@
x += width + mHorizontalSpacing;
}
}
- y += maxHeight;
- if (row > 0) {
- y += mVerticalSpacing;
- }
+ y += maxHeight + mVerticalSpacing;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e42c47b..ee59ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -770,6 +770,8 @@
public void onAnimationEnd(Animator animation) {
mHeaderAnimating = false;
updateQsState();
+ // Unset the listener, otherwise this may persist for another view property animation
+ getView().animate().setListener(null);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 71eb4a2..d69deef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -26,6 +26,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -103,6 +104,8 @@
protected LinearLayout mHorizontalContentContainer;
protected QSTileLayout mTileLayout;
+ private float mSquishinessFraction = 1f;
+ private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -179,10 +182,26 @@
if (mTileLayout == null) {
mTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
.inflate(R.layout.qs_paged_tile_layout, this, false);
+ mTileLayout.setSquishinessFraction(mSquishinessFraction);
}
return mTileLayout;
}
+ public void setSquishinessFraction(float squishinessFraction) {
+ if (Float.compare(squishinessFraction, mSquishinessFraction) == 0) {
+ return;
+ }
+ mSquishinessFraction = squishinessFraction;
+ if (mTileLayout == null) {
+ return;
+ }
+ mTileLayout.setSquishinessFraction(squishinessFraction);
+ if (getMeasuredWidth() == 0) {
+ return;
+ }
+ updateViewPositions();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mTileLayout instanceof PagedTileLayout) {
@@ -228,6 +247,39 @@
setMeasuredDimension(getMeasuredWidth(), height);
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ mChildrenLayoutTop.put(child, child.getTop());
+ }
+ updateViewPositions();
+ }
+
+ private void updateViewPositions() {
+ if (!(mTileLayout instanceof TileLayout)) {
+ return;
+ }
+ TileLayout layout = (TileLayout) mTileLayout;
+
+ // Adjust view positions based on tile squishing
+ int tileHeightOffset = layout.getTilesHeight() - layout.getHeight();
+
+ boolean move = false;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (move) {
+ int top = mChildrenLayoutTop.get(child);
+ child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset,
+ child.getRight(), top + tileHeightOffset + child.getHeight());
+ }
+ if (child == mTileLayout) {
+ move = true;
+ }
+ }
+ }
+
protected String getDumpableTag() {
return TAG;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index f7d1b1e..eddc206 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -147,6 +147,10 @@
return mMediaHost;
}
+ public void setSquishinessFraction(float squishinessFraction) {
+ mView.setSquishinessFraction(squishinessFraction);
+ }
+
@Override
protected void onViewAttached() {
mQsTileRevealController = createTileRevealController();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
index c1c146d..c680cb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
@@ -1,14 +1,10 @@
package com.android.systemui.qs
-import android.view.ViewGroup
-import com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER
import com.android.systemui.qs.dagger.QSScope
import javax.inject.Inject
-import javax.inject.Named
@QSScope
class QSSquishinessController @Inject constructor(
- @Named(QQS_FOOTER) private val qqsFooterActionsView: FooterActionsView,
private val qsAnimator: QSAnimator,
private val qsPanelController: QSPanelController,
private val quickQSPanelController: QuickQSPanelController
@@ -33,23 +29,7 @@
* Change the height of all tiles and repositions their siblings.
*/
private fun updateSquishiness() {
- (qsPanelController.tileLayout as QSPanel.QSTileLayout).setSquishinessFraction(squishiness)
- val tileLayout = quickQSPanelController.tileLayout as TileLayout
- tileLayout.setSquishinessFraction(squishiness)
-
- // Calculate how much we should move the footer
- val tileHeightOffset = tileLayout.height - tileLayout.tilesHeight
- val footerTopMargin = (qqsFooterActionsView.layoutParams as ViewGroup.MarginLayoutParams)
- .topMargin
- val nextTop = tileLayout.bottom - tileHeightOffset + footerTopMargin
- val amountMoved = nextTop - qqsFooterActionsView.top
-
- // Move the footer and other siblings (MediaPlayer)
- (qqsFooterActionsView.parent as ViewGroup?)?.let { parent ->
- val index = parent.indexOfChild(qqsFooterActionsView)
- for (i in index until parent.childCount) {
- parent.getChildAt(i).top += amountMoved
- }
- }
+ qsPanelController.setSquishinessFraction(squishiness)
+ quickQSPanelController.setSquishinessFraction(squishiness)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7770d8e..8b94983 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -374,7 +374,6 @@
}
void setChipVisibility(boolean visibility) {
- mPrivacyChip.setVisibility(visibility ? View.VISIBLE : View.GONE);
if (visibility) {
// Animates the icons and battery indicator from alpha 0 to 1, when the chip is visible
mIconsAlphaAnimator = mIconsAlphaAnimatorFixed;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 67fdf86..2c6972a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -17,13 +17,8 @@
package com.android.systemui.qs;
import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-
-import androidx.annotation.NonNull;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -31,13 +26,6 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.privacy.PrivacyChipEvent;
-import com.android.systemui.privacy.PrivacyDialogController;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
@@ -55,23 +43,17 @@
* Controller for {@link QuickStatusBarHeader}.
*/
@QSScope
-class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
- private static final String TAG = "QuickStatusBarHeader";
+class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> implements
+ ChipVisibilityListener {
- private final PrivacyItemController mPrivacyItemController;
- private final ActivityStarter mActivityStarter;
- private final UiEventLogger mUiEventLogger;
private final QSCarrierGroupController mQSCarrierGroupController;
private final QuickQSPanelController mQuickQSPanelController;
- private final OngoingPrivacyChip mPrivacyChip;
private final Clock mClockView;
private final StatusBarIconController mStatusBarIconController;
private final DemoModeController mDemoModeController;
private final StatusIconContainer mIconContainer;
private final StatusBarIconController.TintedIconManager mIconManager;
private final DemoMode mDemoModeReceiver;
- private final PrivacyLogger mPrivacyLogger;
- private final PrivacyDialogController mPrivacyDialogController;
private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private final FeatureFlags mFeatureFlags;
private final BatteryMeterViewController mBatteryMeterViewController;
@@ -79,83 +61,31 @@
private final VariableDateViewController mVariableDateViewControllerDateView;
private final VariableDateViewController mVariableDateViewControllerClockDateView;
+ private final HeaderPrivacyIconsController mPrivacyIconsController;
private boolean mListening;
- private boolean mMicCameraIndicatorsEnabled;
- private boolean mLocationIndicatorsEnabled;
- private boolean mPrivacyChipLogged;
- private final String mCameraSlot;
- private final String mMicSlot;
- private final String mLocationSlot;
private SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
- private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
- @Override
- public void onPrivacyItemsChanged(@NonNull List<PrivacyItem> privacyItems) {
- mPrivacyChip.setPrivacyList(privacyItems);
- setChipVisibility(!privacyItems.isEmpty());
- }
-
- @Override
- public void onFlagMicCameraChanged(boolean flag) {
- if (mMicCameraIndicatorsEnabled != flag) {
- mMicCameraIndicatorsEnabled = flag;
- update();
- }
- }
-
- @Override
- public void onFlagLocationChanged(boolean flag) {
- if (mLocationIndicatorsEnabled != flag) {
- mLocationIndicatorsEnabled = flag;
- update();
- }
- }
-
- private void update() {
- updatePrivacyIconSlots();
- setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
- }
- };
-
- private View.OnClickListener mOnClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (v == mPrivacyChip) {
- // If the privacy chip is visible, it means there were some indicators
- mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
- mPrivacyDialogController.showDialog(getContext());
- }
- }
- };
-
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
- PrivacyItemController privacyItemController,
- ActivityStarter activityStarter, UiEventLogger uiEventLogger,
+ HeaderPrivacyIconsController headerPrivacyIconsController,
StatusBarIconController statusBarIconController,
DemoModeController demoModeController,
QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
- PrivacyLogger privacyLogger,
SysuiColorExtractor colorExtractor,
- PrivacyDialogController privacyDialogController,
QSExpansionPathInterpolator qsExpansionPathInterpolator,
FeatureFlags featureFlags,
VariableDateViewController.Factory variableDateViewControllerFactory,
BatteryMeterViewController batteryMeterViewController,
StatusBarContentInsetsProvider statusBarContentInsetsProvider) {
super(view);
- mPrivacyItemController = privacyItemController;
- mActivityStarter = activityStarter;
- mUiEventLogger = uiEventLogger;
+ mPrivacyIconsController = headerPrivacyIconsController;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
mQuickQSPanelController = quickQSPanelController;
- mPrivacyLogger = privacyLogger;
- mPrivacyDialogController = privacyDialogController;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mFeatureFlags = featureFlags;
mBatteryMeterViewController = batteryMeterViewController;
@@ -164,8 +94,6 @@
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
.build();
-
- mPrivacyChip = mView.findViewById(R.id.privacy_chip);
mClockView = mView.findViewById(R.id.clock);
mIconContainer = mView.findViewById(R.id.statusIcons);
mVariableDateViewControllerDateView = variableDateViewControllerFactory.create(
@@ -184,10 +112,6 @@
};
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
- mCameraSlot = getResources().getString(com.android.internal.R.string.status_bar_camera);
- mMicSlot = getResources().getString(com.android.internal.R.string.status_bar_microphone);
- mLocationSlot = getResources().getString(com.android.internal.R.string.status_bar_location);
-
// Don't need to worry about tuner settings for this icon
mBatteryMeterViewController.ignoreTunerUpdates();
}
@@ -199,20 +123,13 @@
@Override
protected void onViewAttached() {
- mPrivacyChip.setOnClickListener(mOnClickListener);
-
- mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
- mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
-
- // Ignore privacy icons because they show in the space above QQS
- updatePrivacyIconSlots();
+ mPrivacyIconsController.onParentVisible();
+ mPrivacyIconsController.setChipVisibilityListener(this);
mIconContainer.addIgnoredSlot(
getResources().getString(com.android.internal.R.string.status_bar_managed_profile));
mIconContainer.setShouldRestrictIcons(false);
mStatusBarIconController.addIconGroup(mIconManager);
- setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
-
mView.setIsSingleCarrier(mQSCarrierGroupController.isSingleCarrier());
mQSCarrierGroupController
.setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
@@ -242,7 +159,7 @@
@Override
protected void onViewDetached() {
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
- mPrivacyChip.setOnClickListener(null);
+ mPrivacyIconsController.onParentInvisible();
mStatusBarIconController.removeIconGroup(mIconManager);
mQSCarrierGroupController.setOnSingleCarrierChangedListener(null);
mDemoModeController.removeCallback(mDemoModeReceiver);
@@ -267,54 +184,15 @@
}
if (listening) {
- // Get the most up to date info
- mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
- mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
- mPrivacyItemController.addCallback(mPICCallback);
+ mPrivacyIconsController.startListening();
} else {
- mPrivacyItemController.removeCallback(mPICCallback);
- mPrivacyChipLogged = false;
+ mPrivacyIconsController.stopListening();
}
}
- private void setChipVisibility(boolean chipVisible) {
- if (chipVisible && getChipEnabled()) {
- mPrivacyLogger.logChipVisible(true);
- // Makes sure that the chip is logged as viewed at most once each time QS is opened
- // mListening makes sure that the callback didn't return after the user closed QS
- if (!mPrivacyChipLogged && mListening) {
- mPrivacyChipLogged = true;
- mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
- }
- } else {
- mPrivacyLogger.logChipVisible(false);
- }
- mView.setChipVisibility(chipVisible);
- }
-
- private void updatePrivacyIconSlots() {
- if (getChipEnabled()) {
- if (mMicCameraIndicatorsEnabled) {
- mIconContainer.addIgnoredSlot(mCameraSlot);
- mIconContainer.addIgnoredSlot(mMicSlot);
- } else {
- mIconContainer.removeIgnoredSlot(mCameraSlot);
- mIconContainer.removeIgnoredSlot(mMicSlot);
- }
- if (mLocationIndicatorsEnabled) {
- mIconContainer.addIgnoredSlot(mLocationSlot);
- } else {
- mIconContainer.removeIgnoredSlot(mLocationSlot);
- }
- } else {
- mIconContainer.removeIgnoredSlot(mCameraSlot);
- mIconContainer.removeIgnoredSlot(mMicSlot);
- mIconContainer.removeIgnoredSlot(mLocationSlot);
- }
- }
-
- private boolean getChipEnabled() {
- return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
+ @Override
+ public void onChipVisibilityRefreshed(boolean visible) {
+ mView.setChipVisibility(visible);
}
public void setContentMargins(int marginStart, int marginEnd) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index b11420a..11e5b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -26,6 +26,7 @@
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.FooterActionsController;
import com.android.systemui.qs.FooterActionsController.ExpansionState;
import com.android.systemui.qs.FooterActionsControllerBuilder;
@@ -39,6 +40,7 @@
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
import javax.inject.Named;
@@ -189,4 +191,18 @@
static boolean providesQSUsingMediaPlayer(Context context) {
return useQsMediaPlayer(context);
}
+
+ /** */
+ @Provides
+ @QSScope
+ static OngoingPrivacyChip providesPrivacyChip(QuickStatusBarHeader qsHeader) {
+ return qsHeader.findViewById(R.id.privacy_chip);
+ }
+
+ /** */
+ @Provides
+ @QSScope
+ static StatusIconContainer providesStatusIconContainer(QuickStatusBarHeader qsHeader) {
+ return qsHeader.findViewById(R.id.statusIcons);
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 6d1bbee..48255b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -34,6 +34,8 @@
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.RunningFgsController;
+import com.android.systemui.statusbar.policy.RunningFgsControllerImpl;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.SecureSettings;
@@ -89,4 +91,9 @@
/** */
@Binds
QSHost provideQsHost(QSTileHost controllerImpl);
+
+ /** */
+ @Binds
+ RunningFgsController provideRunningFgsController(
+ RunningFgsControllerImpl runningFgsController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
index baf3018..11c4949 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -18,11 +18,8 @@
import android.content.Context
import android.graphics.drawable.Icon
-import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.view.WindowInsets
import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.plugins.qs.QSTile
@@ -38,25 +35,12 @@
*/
class TileRequestDialog(
context: Context
-) : SystemUIDialog(context, R.style.TileRequestDialog) {
+) : SystemUIDialog(context) {
companion object {
internal val CONTENT_ID = R.id.content
}
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- window?.apply {
- attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
- attributes.receiveInsetsIgnoringZOrder = true
- setLayout(
- context.resources
- .getDimensionPixelSize(R.dimen.qs_tile_service_request_dialog_width),
- WRAP_CONTENT
- )
- }
- }
-
/**
* Set the data of the tile to add, to show the user.
*/
@@ -76,9 +60,7 @@
context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
)
}
- val spacing = context.resources.getDimensionPixelSize(
- R.dimen.qs_tile_service_request_content_space
- )
+ val spacing = 0
setView(ll, spacing, spacing, spacing, spacing / 2)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialogEventLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialogEventLogger.kt
new file mode 100644
index 0000000..e0f9cc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialogEventLogger.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.qs.external
+
+import android.app.StatusBarManager
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.UiEventLoggerImpl
+
+class TileRequestDialogEventLogger @VisibleForTesting constructor(
+ private val uiEventLogger: UiEventLogger,
+ private val instanceIdSequence: InstanceIdSequence
+) {
+ companion object {
+ const val MAX_INSTANCE_ID = 1 shl 20
+ }
+
+ constructor() : this(UiEventLoggerImpl(), InstanceIdSequence(MAX_INSTANCE_ID))
+
+ /**
+ * Obtain a new [InstanceId] to log a session for a dialog request.
+ */
+ fun newInstanceId(): InstanceId = instanceIdSequence.newInstanceId()
+
+ /**
+ * Log that the dialog has been shown to the user for a tile in the given [packageName]. This
+ * call should use a new [instanceId].
+ */
+ fun logDialogShown(packageName: String, instanceId: InstanceId) {
+ uiEventLogger.logWithInstanceId(
+ TileRequestDialogEvent.TILE_REQUEST_DIALOG_SHOWN,
+ /* uid */ 0,
+ packageName,
+ instanceId
+ )
+ }
+
+ /**
+ * Log the user response to the dialog being shown. Must follow a call to [logDialogShown] that
+ * used the same [packageName] and [instanceId]. Only the following responses are valid:
+ * * [StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED]
+ * * [StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED]
+ * * [StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED]
+ */
+ fun logUserResponse(
+ @StatusBarManager.RequestResult response: Int,
+ packageName: String,
+ instanceId: InstanceId
+ ) {
+ val event = when (response) {
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED -> {
+ TileRequestDialogEvent.TILE_REQUEST_DIALOG_DISMISSED
+ }
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED -> {
+ TileRequestDialogEvent.TILE_REQUEST_DIALOG_TILE_NOT_ADDED
+ }
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED -> {
+ TileRequestDialogEvent.TILE_REQUEST_DIALOG_TILE_ADDED
+ }
+ else -> {
+ throw IllegalArgumentException("User response not valid: $response")
+ }
+ }
+ uiEventLogger.logWithInstanceId(event, /* uid */ 0, packageName, instanceId)
+ }
+
+ /**
+ * Log that the dialog will not be shown because the tile was already part of the active set.
+ * Corresponds to a response of [StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED].
+ */
+ fun logTileAlreadyAdded(packageName: String, instanceId: InstanceId) {
+ uiEventLogger.logWithInstanceId(
+ TileRequestDialogEvent.TILE_REQUEST_DIALOG_TILE_ALREADY_ADDED,
+ /* uid */ 0,
+ packageName,
+ instanceId
+ )
+ }
+}
+
+enum class TileRequestDialogEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "Tile request dialog not shown because tile is already added.")
+ TILE_REQUEST_DIALOG_TILE_ALREADY_ADDED(917),
+
+ @UiEvent(doc = "Tile request dialog shown to user.")
+ TILE_REQUEST_DIALOG_SHOWN(918),
+
+ @UiEvent(doc = "User dismisses dialog without choosing an option.")
+ TILE_REQUEST_DIALOG_DISMISSED(919),
+
+ @UiEvent(doc = "User accepts adding tile from dialog.")
+ TILE_REQUEST_DIALOG_TILE_ADDED(920),
+
+ @UiEvent(doc = "User denies adding tile from dialog.")
+ TILE_REQUEST_DIALOG_TILE_NOT_ADDED(921);
+
+ override fun getId() = _id
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
index 210ee93..73d6b97 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
@@ -44,6 +44,7 @@
private val qsTileHost: QSTileHost,
private val commandQueue: CommandQueue,
private val commandRegistry: CommandRegistry,
+ private val eventLogger: TileRequestDialogEventLogger,
private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsTileHost.context) }
) {
@@ -97,25 +98,31 @@
icon: Icon?,
callback: Consumer<Int>
) {
+ val instanceId = eventLogger.newInstanceId()
+ val packageName = componentName.packageName
if (isTileAlreadyAdded(componentName)) {
callback.accept(TILE_ALREADY_ADDED)
+ eventLogger.logTileAlreadyAdded(packageName, instanceId)
return
}
val dialogResponse = Consumer<Int> { response ->
if (response == ADD_TILE) {
addTile(componentName)
}
+ dialogCanceller = null
+ eventLogger.logUserResponse(response, packageName, instanceId)
callback.accept(response)
}
val tileData = TileRequestDialog.TileData(appName, label, icon)
createDialog(tileData, dialogResponse).also { dialog ->
dialogCanceller = {
- if (componentName.packageName == it) {
+ if (packageName == it) {
dialog.cancel()
}
dialogCanceller = null
}
}.show()
+ eventLogger.logDialogShown(packageName, instanceId)
}
private fun createDialog(
@@ -168,7 +175,12 @@
private val commandRegistry: CommandRegistry
) {
fun create(qsTileHost: QSTileHost): TileServiceRequestController {
- return TileServiceRequestController(qsTileHost, commandQueue, commandRegistry)
+ return TileServiceRequestController(
+ qsTileHost,
+ commandQueue,
+ commandRegistry,
+ TileRequestDialogEventLogger()
+ )
}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 16be998..ac95bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -36,6 +36,7 @@
import com.android.systemui.qs.tiles.DataSaverTile;
import com.android.systemui.qs.tiles.DeviceControlsTile;
import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.qs.tiles.FgsManagerTile;
import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.HotspotTile;
import com.android.systemui.qs.tiles.InternetTile;
@@ -94,6 +95,7 @@
private final Provider<QuickAccessWalletTile> mQuickAccessWalletTileProvider;
private final Provider<QRCodeScannerTile> mQRCodeScannerTileProvider;
private final Provider<OneHandedModeTile> mOneHandedModeTileProvider;
+ private final Provider<FgsManagerTile> mFgsManagerTileProvider;
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@@ -130,7 +132,8 @@
Provider<AlarmTile> alarmTileProvider,
Provider<QuickAccessWalletTile> quickAccessWalletTileProvider,
Provider<QRCodeScannerTile> qrCodeScannerTileProvider,
- Provider<OneHandedModeTile> oneHandedModeTileProvider) {
+ Provider<OneHandedModeTile> oneHandedModeTileProvider,
+ Provider<FgsManagerTile> fgsManagerTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -163,6 +166,7 @@
mQuickAccessWalletTileProvider = quickAccessWalletTileProvider;
mQRCodeScannerTileProvider = qrCodeScannerTileProvider;
mOneHandedModeTileProvider = oneHandedModeTileProvider;
+ mFgsManagerTileProvider = fgsManagerTileProvider;
}
public QSTile createTile(String tileSpec) {
@@ -233,6 +237,8 @@
return mQRCodeScannerTileProvider.get();
case "onehanded":
return mOneHandedModeTileProvider.get();
+ case "fgsmanager":
+ return mFgsManagerTileProvider.get();
}
// Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index b1cd03c..106a1b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -253,7 +253,7 @@
return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
case Tile.STATE_ACTIVE:
return Utils.getColorAttrDefaultColor(context,
- android.R.attr.textColorPrimaryInverse);
+ com.android.internal.R.attr.textColorOnAccent);
default:
Log.e("QSIconView", "Invalid state " + state);
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 4bdeb56..6bb79867 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -88,7 +88,7 @@
private val colorUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorInactive)
private val colorLabelActive =
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse)
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorOnAccent)
private val colorLabelInactive =
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
private val colorLabelUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorLabelInactive)
@@ -247,7 +247,10 @@
} else {
measuredHeight
}
- bottom = top + (actualHeight * squishinessFraction).toInt()
+ // Limit how much we affect the height, so we don't have rounding artifacts when the tile
+ // is too short.
+ val constrainedSquishiness = 0.1f + squishinessFraction * 0.9f
+ bottom = top + (actualHeight * constrainedSquishiness).toInt()
scrollY = (actualHeight - height) / 2
}
@@ -649,7 +652,8 @@
"wallet" to R.array.tile_states_wallet,
"qr_code_scanner" to R.array.tile_states_qr_code_scanner,
"alarm" to R.array.tile_states_alarm,
- "onehanded" to R.array.tile_states_onehanded
+ "onehanded" to R.array.tile_states_onehanded,
+ "fgsmanager" to R.array.tile_states_fgsmanager
)
fun getSubtitleId(spec: String?): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 0427e38..b83dc52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -31,7 +31,6 @@
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
-import android.view.WindowManager.LayoutParams;
import android.widget.Button;
import androidx.annotation.Nullable;
@@ -40,6 +39,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -76,6 +76,7 @@
private final CastDetailAdapter mDetailAdapter;
private final KeyguardStateController mKeyguard;
private final NetworkController mNetworkController;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private final Callback mCallback = new Callback();
private Dialog mDialog;
private boolean mWifiConnected;
@@ -94,7 +95,8 @@
CastController castController,
KeyguardStateController keyguardStateController,
NetworkController networkController,
- HotspotController hotspotController
+ HotspotController hotspotController,
+ DialogLaunchAnimator dialogLaunchAnimator
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -102,6 +104,7 @@
mDetailAdapter = new CastDetailAdapter();
mKeyguard = keyguardStateController;
mNetworkController = networkController;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
mNetworkController.observe(this, mSignalCallback);
@@ -153,9 +156,15 @@
List<CastDevice> activeDevices = getActiveDevices();
if (willPopDetail()) {
- mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
- showDetail(true);
- });
+ if (!mKeyguard.isShowing()) {
+ showDetail(view);
+ } else {
+ mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+ // Dismissing the keyguard will collapse the shade, so we don't animate from the
+ // view here as it would not look good.
+ showDetail(null /* view */);
+ });
+ }
} else {
mController.stopCasting(activeDevices.get(0));
}
@@ -184,19 +193,29 @@
@Override
public void showDetail(boolean show) {
+ showDetail(null /* view */);
+ }
+
+ private void showDetail(@Nullable View view) {
mUiHandler.post(() -> {
mDialog = MediaRouteDialogPresenter.createDialog(mContext, ROUTE_TYPE_REMOTE_DISPLAY,
v -> {
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
mActivityStarter
.postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
});
- mDialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
SystemUIDialog.setShowForAllUsers(mDialog, true);
SystemUIDialog.registerDismissListener(mDialog);
SystemUIDialog.setWindowOnTop(mDialog);
- mUiHandler.post(() -> mDialog.show());
- mHost.collapsePanels();
+
+ mUiHandler.post(() -> {
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(mDialog, view);
+ } else {
+ mDialog.show();
+ }
+ });
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 99cb700..83506b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -19,6 +19,7 @@
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
@@ -41,7 +42,6 @@
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.widget.Switch;
import android.widget.Toast;
@@ -53,6 +53,7 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysUIToast;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -84,6 +85,7 @@
private final DndDetailAdapter mDetailAdapter;
private final SharedPreferences mSharedPreferences;
private final SettingObserver mSettingZenDuration;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private boolean mListening;
private boolean mShowingDetail;
@@ -100,7 +102,8 @@
QSLogger qsLogger,
ZenModeController zenModeController,
@Main SharedPreferences sharedPreferences,
- SecureSettings secureSettings
+ SecureSettings secureSettings,
+ DialogLaunchAnimator dialogLaunchAnimator
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -108,6 +111,7 @@
mSharedPreferences = sharedPreferences;
mDetailAdapter = new DndDetailAdapter();
mController.observe(getLifecycle(), mZenCallback);
+ mDialogLaunchAnimator = dialogLaunchAnimator;
mSettingZenDuration = new SettingObserver(secureSettings, mUiHandler,
Settings.Secure.ZEN_DURATION, getHost().getUserId()) {
@Override
@@ -117,8 +121,6 @@
};
}
-
-
public static void setVisible(Context context, boolean visible) {
Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
}
@@ -187,14 +189,13 @@
switch (zenDuration) {
case Settings.Secure.ZEN_DURATION_PROMPT:
mUiHandler.post(() -> {
- Dialog mDialog = new EnableZenModeDialog(mContext).createDialog();
- mDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- SystemUIDialog.setShowForAllUsers(mDialog, true);
- SystemUIDialog.registerDismissListener(mDialog);
- SystemUIDialog.setWindowOnTop(mDialog);
- mUiHandler.post(() -> mDialog.show());
- mHost.collapsePanels();
+ Dialog dialog = makeZenModeDialog();
+ SystemUIDialog.registerDismissListener(dialog);
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(dialog, view, false);
+ } else {
+ dialog.show();
+ }
});
break;
case Settings.Secure.ZEN_DURATION_FOREVER:
@@ -209,6 +210,14 @@
}
}
+ private Dialog makeZenModeDialog() {
+ AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog)
+ .createDialog();
+ SystemUIDialog.applyFlags(dialog);
+ SystemUIDialog.setShowForAllUsers(dialog, true);
+ return dialog;
+ }
+
@Override
protected void handleSecondaryClick(@Nullable View view) {
if (mController.isVolumeRestricted()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
new file mode 100644
index 0000000..75cf4d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.qs.tiles
+
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.provider.DeviceConfig
+import android.view.View
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.DejankUtils
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.fgsmanager.FgsManagerDialogFactory
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.RunningFgsController
+import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Quicksettings tile for the foreground services manager (task manager)
+ */
+class FgsManagerTile @Inject constructor(
+ host: QSHost?,
+ @Background backgroundLooper: Looper?,
+ @Background private val backgroundExecutor: Executor?,
+ @Main mainHandler: Handler?,
+ falsingManager: FalsingManager?,
+ metricsLogger: MetricsLogger?,
+ statusBarStateController: StatusBarStateController?,
+ activityStarter: ActivityStarter?,
+ qsLogger: QSLogger?,
+ private val fgsManagerDialogFactory: FgsManagerDialogFactory,
+ private val runningFgsController: RunningFgsController
+) : QSTileImpl<QSTile.State?>(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger), RunningFgsController.Callback {
+
+ override fun handleInitialize() {
+ super.handleInitialize()
+ mUiHandler.post { runningFgsController.observe(lifecycle, this) }
+ }
+
+ override fun isAvailable(): Boolean {
+ return DejankUtils.whitelistIpcs<Boolean> {
+ DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, false)
+ }
+ }
+
+ override fun newTileState(): QSTile.State {
+ return QSTile.State()
+ }
+
+ override fun handleClick(view: View?) {
+ mUiHandler.post { fgsManagerDialogFactory.create(view) }
+ }
+
+ override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
+ state?.label = tileLabel
+ state?.secondaryLabel = runningFgsController.getPackagesWithFgs().size.toString()
+ state?.handlesLongClick = false
+ state?.icon = ResourceIcon.get(R.drawable.ic_list)
+ }
+
+ override fun getMetricsCategory(): Int = 0
+
+ override fun getLongClickIntent(): Intent? = null
+
+ // Inline the string so we don't waste translator time since this isn't used in the mocks.
+ // TODO If mocks change need to remember to move this to strings.xml
+ override fun getTileLabel(): CharSequence = "Active apps"
+
+ override fun onFgsPackagesChanged(packages: List<UserPackageTime>) = refreshState()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
index 521dbe7..7e17124 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
@@ -105,8 +105,7 @@
@Override
public Intent getLongClickIntent() {
- // TODO(b/201743873) define new intent action ACTION_ONE_HANDED_SETTINGS in Settings.
- return null;
+ return new Intent(Settings.ACTION_ONE_HANDED_SETTINGS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 77b9cc1..033fe1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -56,6 +56,7 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
@@ -120,6 +121,7 @@
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
private Switch mMobileDataToggle;
+ private View mMobileToggleDivider;
private Switch mWiFiToggle;
private FrameLayout mDoneLayout;
private Drawable mBackgroundOn;
@@ -128,6 +130,7 @@
private boolean mCanConfigMobileData;
// Wi-Fi entries
+ private int mWifiNetworkHeight;
@VisibleForTesting
protected WifiEntry mConnectedWifiEntry;
@VisibleForTesting
@@ -185,6 +188,9 @@
window.setWindowAnimations(R.style.Animation_InternetDialog);
+ mWifiNetworkHeight = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height);
+
mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog);
mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
@@ -207,6 +213,7 @@
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+ mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
@@ -332,9 +339,6 @@
mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
mWiFiToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
- if (isChecked) {
- mWifiScanNotifyLayout.setVisibility(View.GONE);
- }
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
});
@@ -378,7 +382,17 @@
mMobileNetworkLayout.setBackground(
isCarrierNetworkConnected ? mBackgroundOn : mBackgroundOff);
+ TypedArray array = mContext.obtainStyledAttributes(
+ R.style.InternetDialog_Divider_Active, new int[]{android.R.attr.background});
+ int dividerColor = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.textColorSecondary);
+ mMobileToggleDivider.setBackgroundColor(isCarrierNetworkConnected
+ ? array.getColor(0, dividerColor) : dividerColor);
+ array.recycle();
+
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+ mMobileToggleDivider.setVisibility(
+ mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
}
}
@@ -416,9 +430,26 @@
mSeeAllLayout.setVisibility(View.GONE);
return;
}
- mWifiRecyclerView.setVisibility(mWifiEntriesCount > 0 ? View.VISIBLE : View.GONE);
- mSeeAllLayout.setVisibility(
- (mConnectedWifiEntry != null || mWifiEntriesCount > 0) ? View.VISIBLE : View.GONE);
+ mWifiRecyclerView.setMinimumHeight(mWifiNetworkHeight * getWifiListMaxCount());
+ mWifiRecyclerView.setVisibility(View.VISIBLE);
+ final boolean showSeeAll = mConnectedWifiEntry != null || mWifiEntriesCount > 0;
+ mSeeAllLayout.setVisibility(showSeeAll ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @VisibleForTesting
+ @MainThread
+ int getWifiListMaxCount() {
+ int count = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+ if (mEthernetLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+ if (mMobileNetworkLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+ if (mConnectedWifListLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+ return count;
}
@MainThread
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
deleted file mode 100644
index 26d1bbd..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
+++ /dev/null
@@ -1,82 +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.qs.user
-
-import android.content.Context
-import android.os.Bundle
-import android.view.Gravity
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowInsets
-import android.view.WindowManager
-import com.android.systemui.qs.PseudoGridView
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.R
-
-/**
- * Dialog for switching users or creating new ones.
- */
-class UserDialog(
- context: Context
-) : SystemUIDialog(context) {
-
- // create() is no-op after creation
- private lateinit var _doneButton: View
- /**
- * Button with text "Done" in dialog.
- */
- val doneButton: View
- get() {
- create()
- return _doneButton
- }
-
- private lateinit var _settingsButton: View
- /**
- * Button with text "User Settings" in dialog.
- */
- val settingsButton: View
- get() {
- create()
- return _settingsButton
- }
-
- private lateinit var _grid: PseudoGridView
- /**
- * Grid to populate with user avatar from adapter
- */
- val grid: ViewGroup
- get() {
- create()
- return _grid
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- window?.apply {
- setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
- attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
- attributes.receiveInsetsIgnoringZOrder = true
- setGravity(Gravity.CENTER)
- }
- setContentView(R.layout.qs_user_dialog_content)
-
- _doneButton = requireViewById(R.id.done)
- _settingsButton = requireViewById(R.id.settings)
- _grid = requireViewById(R.id.grid)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index d74a50e..7c8f4b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -21,13 +21,16 @@
import android.content.DialogInterface
import android.content.Intent
import android.provider.Settings
+import android.view.LayoutInflater
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
import javax.inject.Provider
@@ -40,7 +43,7 @@
private val activityStarter: ActivityStarter,
private val falsingManager: FalsingManager,
private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val dialogFactory: (Context) -> UserDialog
+ private val dialogFactory: (Context) -> SystemUIDialog
) {
@Inject
@@ -54,7 +57,7 @@
activityStarter,
falsingManager,
dialogLaunchAnimator,
- { UserDialog(it) }
+ { SystemUIDialog(it) }
)
companion object {
@@ -71,9 +74,10 @@
with(dialogFactory(view.context)) {
setShowForAllUsers(true)
setCanceledOnTouchOutside(true)
- create() // Needs to be called before we can retrieve views
- settingsButton.setOnClickListener {
+ setTitle(R.string.qs_user_switch_dialog_title)
+ setPositiveButton(R.string.quick_settings_done, null)
+ setNeutralButton(R.string.quick_settings_more_user_settings) { _, _ ->
if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
activityStarter.postStartActivityDismissingKeyguard(
@@ -81,31 +85,33 @@
0
)
}
- dismiss()
}
- doneButton.setOnClickListener { dismiss() }
+ val gridFrame = LayoutInflater.from(this.context)
+ .inflate(R.layout.qs_user_dialog_content, null)
+ setView(gridFrame)
val adapter = userDetailViewAdapterProvider.get()
- adapter.linkToViewGroup(grid)
- val hostDialog = dialogLaunchAnimator.showFromView(this, view)
- adapter.injectDialogShower(DialogShowerImpl(hostDialog, dialogLaunchAnimator))
+ adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
+
+ dialogLaunchAnimator.showFromView(this, view)
+ adapter.injectDialogShower(DialogShowerImpl(this, dialogLaunchAnimator))
}
}
private class DialogShowerImpl(
- private val hostDialog: Dialog,
+ private val animateFrom: Dialog,
private val dialogLaunchAnimator: DialogLaunchAnimator
- ) : DialogInterface by hostDialog, DialogShower {
- override fun showDialog(dialog: Dialog): Dialog {
- return dialogLaunchAnimator.showFromDialog(
+ ) : DialogInterface by animateFrom, DialogShower {
+ override fun showDialog(dialog: Dialog) {
+ dialogLaunchAnimator.showFromDialog(
dialog,
- parentHostDialog = hostDialog
+ animateFrom = animateFrom
)
}
}
interface DialogShower : DialogInterface {
- fun showDialog(dialog: Dialog): Dialog
+ fun showDialog(dialog: Dialog)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index fa874b1..3ed7e84 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -986,6 +986,18 @@
}
}
+ public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy to update nav buttons dark intensity");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onNavButtonsDarkIntensityChanged()", e);
+ }
+ }
+
private void updateEnabledState() {
final int currentUser = ActivityManagerWrapper.getInstance().getCurrentUserId();
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index a7f8bca..7bcaf5f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -86,6 +86,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
+import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.QuickStepContract;
@@ -162,6 +163,7 @@
private GestureDetector mSwipeDetector;
private SwipeDismissHandler mSwipeDismissHandler;
private InputMonitorCompat mInputMonitor;
+ private InputChannelCompat.InputEventReceiver mInputEventReceiver;
private boolean mShowScrollablePreview;
private String mPackageName = "";
@@ -302,8 +304,8 @@
private void startInputListening() {
stopInputListening();
mInputMonitor = new InputMonitorCompat("Screenshot", Display.DEFAULT_DISPLAY);
- mInputMonitor.getInputReceiver(Looper.getMainLooper(), Choreographer.getInstance(),
- ev -> {
+ mInputEventReceiver = mInputMonitor.getInputReceiver(
+ Looper.getMainLooper(), Choreographer.getInstance(), ev -> {
if (ev instanceof MotionEvent) {
MotionEvent event = (MotionEvent) ev;
if (event.getActionMasked() == MotionEvent.ACTION_DOWN
@@ -320,6 +322,10 @@
mInputMonitor.dispose();
mInputMonitor = null;
}
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ }
}
@Override // ViewGroup
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 75b3592..3cecbb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -533,9 +533,13 @@
* @param animate {@code true} to show animations.
*/
public void recomputeDisableFlags(int displayId, boolean animate) {
- int disabled1 = getDisabled1(displayId);
- int disabled2 = getDisabled2(displayId);
- disable(displayId, disabled1, disabled2, animate);
+ // This must update holding the lock otherwise it can clobber the disabled flags set on the
+ // binder thread from the disable() call
+ synchronized (mLock) {
+ int disabled1 = getDisabled1(displayId);
+ int disabled2 = getDisabled2(displayId);
+ disable(displayId, disabled1, disabled2, animate);
+ }
}
private void setDisabled(int displayId, int disabled1, int disabled2) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 190c773e..f23a7ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -717,6 +717,9 @@
textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
ViewClippingUtil.setClippingDeactivated(textView, false,
mClippingParams);
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ textView.animate().setListener(null);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 5635f65..2b5453a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -33,6 +33,7 @@
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
@@ -66,6 +67,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
+import com.android.systemui.util.DumpUtilsKt;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -653,9 +655,19 @@
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pwOriginal, String[] args) {
+ IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
+ if (mRemoteInputController != null) {
+ pw.println("mRemoteInputController: " + mRemoteInputController);
+ pw.increaseIndent();
+ mRemoteInputController.dump(pw);
+ pw.decreaseIndent();
+ }
if (mRemoteInputListener instanceof Dumpable) {
+ pw.println("mRemoteInputListener: " + mRemoteInputListener.getClass().getSimpleName());
+ pw.increaseIndent();
((Dumpable) mRemoteInputListener).dump(fd, pw, args);
+ pw.decreaseIndent();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index ecde001..abfdfaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
+
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
@@ -39,6 +41,8 @@
import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
+import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -97,6 +101,7 @@
private final Context mContext;
private NotificationPresenter mPresenter;
+ private NotifStackController mStackController;
private NotificationListContainer mListContainer;
// Used to help track down re-entrant calls to our update methods, which will cause bugs.
@@ -147,8 +152,10 @@
}
public void setUpWithPresenter(NotificationPresenter presenter,
+ NotifStackController stackController,
NotificationListContainer listContainer) {
mPresenter = presenter;
+ mStackController = stackController;
mListContainer = listContainer;
if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
mDynamicPrivacyController.addListener(this);
@@ -328,6 +335,7 @@
mTmpChildOrderMap.clear();
updateRowStatesInternal();
+ updateNotifStats();
mListContainer.onNotificationViewUpdateFinished();
@@ -335,6 +343,56 @@
}
/**
+ * In the spirit of unidirectional data flow, calculate this information when the notification
+ * views are updated, and set it once, speeding up lookups later.
+ * This is analogous to logic in the
+ * {@link com.android.systemui.statusbar.notification.collection.coordinator.StackCoordinator}
+ */
+ private void updateNotifStats() {
+ boolean hasNonClearableAlertingNotifs = false;
+ boolean hasClearableAlertingNotifs = false;
+ boolean hasNonClearableSilentNotifs = false;
+ boolean hasClearableSilentNotifs = false;
+ final int childCount = mListContainer.getContainerChildCount();
+ int visibleTopLevelEntries = 0;
+ for (int i = 0; i < childCount; i++) {
+ View child = mListContainer.getContainerChildAt(i);
+ if (child == null || child.getVisibility() == View.GONE) {
+ continue;
+ }
+ if (!(child instanceof ExpandableNotificationRow)) {
+ continue;
+ }
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ boolean isSilent = row.getEntry().getBucket() == BUCKET_SILENT;
+ // NOTE: NotificationEntry.isClearable() will internally check group children to ensure
+ // the group itself definitively clearable.
+ boolean isClearable = row.getEntry().isClearable();
+ visibleTopLevelEntries++;
+ if (isSilent) {
+ if (isClearable) {
+ hasClearableSilentNotifs = true;
+ } else { // !isClearable
+ hasNonClearableSilentNotifs = true;
+ }
+ } else { // !isSilent
+ if (isClearable) {
+ hasClearableAlertingNotifs = true;
+ } else { // !isClearable
+ hasNonClearableAlertingNotifs = true;
+ }
+ }
+ }
+ mStackController.setNotifStats(new NotifStats(
+ visibleTopLevelEntries /* numActiveNotifs */,
+ hasNonClearableAlertingNotifs /* hasNonClearableAlertingNotifs */,
+ hasClearableAlertingNotifs /* hasClearableAlertingNotifs */,
+ hasNonClearableSilentNotifs /* hasNonClearableSilentNotifs */,
+ hasClearableSilentNotifs /* hasClearableSilentNotifs */
+ ));
+ }
+
+ /**
* Should a notification entry from the active list be suppressed and not show?
*/
private boolean shouldSuppressActiveNotification(NotificationEntry ent) {
@@ -515,8 +573,7 @@
stack.push(notificationChildren.get(i));
}
}
- row.showFeedbackIcon(mAssistantFeedbackController.showFeedbackIndicator(entry),
- mAssistantFeedbackController.getFeedbackResources(entry));
+ row.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry));
row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
}
@@ -528,9 +585,7 @@
@Override
public void onDynamicPrivacyChanged() {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- throw new IllegalStateException("Old pipeline code running w/ new pipeline enabled");
- }
+ mNotifPipelineFlags.assertLegacyPipelineEnabled();
if (mPerformingUpdate) {
Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index cde3b0e..31ab6bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -23,11 +23,15 @@
import android.os.SystemProperties;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.Pair;
+import androidx.annotation.NonNull;
+
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
+import com.android.systemui.util.DumpUtilsKt;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -293,6 +297,28 @@
mRemoteInputUriController.grantInlineReplyUriPermission(sbn, data);
}
+ /** dump debug info; called by {@link NotificationRemoteInputManager} */
+ public void dump(@NonNull IndentingPrintWriter pw) {
+ pw.print("isRemoteInputActive: ");
+ pw.println(isRemoteInputActive()); // Note that this prunes the mOpen list, printed later.
+ pw.println("mOpen: " + mOpen.size());
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
+ for (Pair<WeakReference<NotificationEntry>, Object> open : mOpen) {
+ NotificationEntry entry = open.first.get();
+ pw.println(entry == null ? "???" : entry.getKey());
+ }
+ });
+ pw.println("mSpinning: " + mSpinning.size());
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
+ for (String key : mSpinning.keySet()) {
+ pw.println(key);
+ }
+ });
+ pw.println(mSpinning);
+ pw.print("mDelegate: ");
+ pw.println(mDelegate);
+ }
+
public interface Callback {
default void onRemoteInputActive(boolean active) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index da2b85e..2dbe59e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -96,6 +96,7 @@
private final ArrayList<RankedListener> mListeners = new ArrayList<>();
private final UiEventLogger mUiEventLogger;
+ private final InteractionJankMonitor mInteractionJankMonitor;
private int mState;
private int mLastState;
private int mUpcomingState;
@@ -149,8 +150,10 @@
private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
@Inject
- public StatusBarStateControllerImpl(UiEventLogger uiEventLogger, DumpManager dumpManager) {
+ public StatusBarStateControllerImpl(UiEventLogger uiEventLogger, DumpManager dumpManager,
+ InteractionJankMonitor interactionJankMonitor) {
mUiEventLogger = uiEventLogger;
+ mInteractionJankMonitor = interactionJankMonitor;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
}
@@ -344,17 +347,23 @@
}
private void beginInteractionJankMonitor() {
- if (mView != null && mView.isAttachedToWindow()) {
- InteractionJankMonitor.getInstance().begin(mView, getCujType());
+ if (mInteractionJankMonitor != null && mView != null && mView.isAttachedToWindow()) {
+ mInteractionJankMonitor.begin(mView, getCujType());
}
}
private void endInteractionJankMonitor() {
- InteractionJankMonitor.getInstance().end(getCujType());
+ if (mInteractionJankMonitor == null) {
+ return;
+ }
+ mInteractionJankMonitor.end(getCujType());
}
private void cancelInteractionJankMonitor() {
- InteractionJankMonitor.getInstance().cancel(getCujType());
+ if (mInteractionJankMonitor == null) {
+ return;
+ }
+ mInteractionJankMonitor.cancel(getCujType());
}
private int getCujType() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 8c54de4..4c7ee17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -20,6 +20,7 @@
import android.app.NotificationManager;
import android.content.Context;
import android.os.Handler;
+import android.service.dreams.IDreamManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -69,7 +70,6 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
@@ -333,7 +333,7 @@
@Provides
@SysUISingleton
static DialogLaunchAnimator provideDialogLaunchAnimator(Context context,
- LaunchAnimator launchAnimator) {
- return new DialogLaunchAnimator(context, launchAnimator, new SystemUIHostDialogProvider());
+ LaunchAnimator launchAnimator, IDreamManager dreamManager) {
+ return new DialogLaunchAnimator(context, launchAnimator, dreamManager);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
index 4b4e513..420dd3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
@@ -24,7 +24,9 @@
import android.content.Context;
import android.os.Handler;
import android.provider.DeviceConfig;
-import android.util.Pair;
+import android.util.SparseArray;
+
+import androidx.annotation.Nullable;
import com.android.internal.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -52,6 +54,8 @@
public static final int STATUS_PROMOTED = 3;
public static final int STATUS_DEMOTED = 4;
+ private final SparseArray<FeedbackIcon> mIcons;
+
private volatile boolean mFeedbackEnabled;
private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
@@ -76,6 +80,16 @@
ENABLE_NAS_FEEDBACK, false);
mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
this::postToHandler, mPropertiesChangedListener);
+ // Populate the array of statuses.
+ mIcons = new SparseArray<>(4);
+ mIcons.set(STATUS_ALERTED, new FeedbackIcon(R.drawable.ic_feedback_alerted,
+ R.string.notification_feedback_indicator_alerted));
+ mIcons.set(STATUS_SILENCED, new FeedbackIcon(R.drawable.ic_feedback_silenced,
+ R.string.notification_feedback_indicator_silenced));
+ mIcons.set(STATUS_PROMOTED, new FeedbackIcon(R.drawable.ic_feedback_uprank,
+ R.string.notification_feedback_indicator_promoted));
+ mIcons.set(STATUS_DEMOTED, new FeedbackIcon(R.drawable.ic_feedback_downrank,
+ R.string.notification_feedback_indicator_demoted));
}
private void postToHandler(Runnable r) {
@@ -120,40 +134,15 @@
}
/**
- * Determines whether to show feedback indicator. The feedback indicator will be shown
- * if {@link #isFeedbackEnabled()} is enabled and assistant has changed this notification's rank
- * or importance.
- *
- * @param entry Notification Entry to show feedback for
- */
- public boolean showFeedbackIndicator(NotificationEntry entry) {
- return getFeedbackStatus(entry) != STATUS_UNCHANGED;
- }
-
- /**
* Get the feedback indicator image and content description resources according to assistant's
* changes on this notification's rank or importance.
*
* @param entry Notification Entry to show feedback for
*/
- public Pair<Integer, Integer> getFeedbackResources(NotificationEntry entry) {
+ @Nullable
+ public FeedbackIcon getFeedbackIcon(NotificationEntry entry) {
int feedbackStatus = getFeedbackStatus(entry);
- switch (feedbackStatus) {
- case STATUS_ALERTED:
- return new Pair(R.drawable.ic_feedback_alerted,
- R.string.notification_feedback_indicator_alerted);
- case STATUS_SILENCED:
- return new Pair(R.drawable.ic_feedback_silenced,
- R.string.notification_feedback_indicator_silenced);
- case STATUS_PROMOTED:
- return new Pair(R.drawable.ic_feedback_uprank,
- R.string.notification_feedback_indicator_promoted);
- case STATUS_DEMOTED:
- return new Pair(R.drawable.ic_feedback_downrank,
- R.string.notification_feedback_indicator_demoted);
- default:
- return new Pair(0, 0);
- }
+ return mIcons.get(feedbackStatus);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/FeedbackIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/FeedbackIcon.kt
new file mode 100644
index 0000000..36b987b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/FeedbackIcon.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.statusbar.notification
+
+import android.annotation.DrawableRes
+import android.annotation.StringRes
+
+/**
+ * The feedback icon to show in the header of a notification.
+ * The icon consists of a drawable and a content description to set on the ImageView.
+ */
+data class FeedbackIcon(
+ /** The drawable resource */
+ @DrawableRes val iconRes: Int,
+ /** The content description string resource */
+ @StringRes val contentDescRes: Int
+)
\ No newline at end of file
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 0fb9fc8..1432f78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -24,11 +24,11 @@
import javax.inject.Inject
class NotifPipelineFlags @Inject constructor(
- val context: Context,
- val featureFlags: FeatureFlags
+ val context: Context,
+ val featureFlags: FeatureFlags
) {
fun checkLegacyPipelineEnabled(): Boolean {
- if (!featureFlags.isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING)) {
+ if (!isNewPipelineEnabled()) {
return true
}
Log.d("NotifPipeline", "Old pipeline code running w/ new pipeline enabled", Exception())
@@ -36,10 +36,16 @@
return false
}
- fun isNewPipelineEnabled(): Boolean = featureFlags.isEnabled(
- Flags.NEW_NOTIFICATION_PIPELINE_RENDERING)
+ fun assertLegacyPipelineEnabled(): Unit =
+ check(!isNewPipelineEnabled()) { "Old pipeline code running w/ new pipeline enabled" }
+
+ fun isNewPipelineEnabled(): Boolean =
+ featureFlags.isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING)
+
+ fun isDevLoggingEnabled(): Boolean =
+ featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
fun isSmartspaceDedupingEnabled(): Boolean =
- featureFlags.isEnabled(Flags.SMARTSPACE)
- && featureFlags.isEnabled(Flags.SMARTSPACE_DEDUPING)
+ featureFlags.isEnabled(Flags.SMARTSPACE) &&
+ featureFlags.isEnabled(Flags.SMARTSPACE_DEDUPING)
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index e78b4f4..0389a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -791,6 +791,7 @@
* these don't exist, although there are a couple exceptions.
*/
public Iterable<NotificationEntry> getPendingNotificationsIterator() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mPendingNotifications.values();
}
@@ -803,6 +804,7 @@
* @return a {@link NotificationEntry} if it has been prepared, else null
*/
public NotificationEntry getActiveNotificationUnfiltered(String key) {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mActiveNotifications.get(key);
}
@@ -811,6 +813,7 @@
* notification doesn't exist.
*/
public NotificationEntry getPendingOrActiveNotif(String key) {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
NotificationEntry entry = mPendingNotifications.get(key);
if (entry != null) {
return entry;
@@ -945,6 +948,7 @@
* @return A read-only list of the currently active notifications
*/
public List<NotificationEntry> getVisibleNotifications() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mReadOnlyNotifications;
}
@@ -954,17 +958,20 @@
*/
@Override
public Collection<NotificationEntry> getAllNotifs() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mReadOnlyAllNotifications;
}
@Nullable
@Override
public NotificationEntry getEntry(String key) {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return getPendingOrActiveNotif(key);
}
/** @return A count of the active notifications */
public int getActiveNotificationsCount() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mReadOnlyNotifications.size();
}
@@ -972,6 +979,7 @@
* @return {@code true} if there is at least one notification that should be visible right now
*/
public boolean hasActiveNotifications() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mReadOnlyNotifications.size() != 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java
new file mode 100644
index 0000000..8d2e3c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java
@@ -0,0 +1,70 @@
+/*
+ * 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.statusbar.notification;
+
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Used to let views that have an alpha not apply the HARDWARE layer type directly, and instead
+ * delegate that to specific children. This is useful if we want to fake not having overlapping
+ * rendering to avoid layer trashing, when fading out a view that is also changing.
+ */
+public interface NotificationFadeAware {
+ /**
+ * Calls {@link View#setLayerType} with {@link View#LAYER_TYPE_HARDWARE} if faded and
+ * {@link View#LAYER_TYPE_NONE} otherwise.
+ */
+ static void setLayerTypeForFaded(@Nullable View view, boolean faded) {
+ if (view != null) {
+ int newLayerType = faded ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE;
+ view.setLayerType(newLayerType, null);
+ }
+ }
+
+ /**
+ * Used like {@link View#setLayerType} with {@link View#LAYER_TYPE_HARDWARE} or
+ * {@link View#LAYER_TYPE_NONE} except that instead of necessarily affecting this view
+ * specifically, this may delegate the call to child views.
+ *
+ * When set to <code>true</code>, the view has two possible paths:
+ * 1. If a hardware layer is required to ensure correct appearance of this view, then
+ * set that layer type.
+ * 2. Otherwise, delegate this call to children, who might make that call for themselves.
+ *
+ * When set to <code>false</code>, the view should undo the above, typically by calling
+ * {@link View#setLayerType} with {@link View#LAYER_TYPE_NONE} on itself and children, and
+ * delegating to this method on children where implemented.
+ *
+ * When this delegates to {@link View#setLayerType} on this view or a subview, `null` will be
+ * passed for the `paint` argument of that call.
+ */
+ void setNotificationFaded(boolean faded);
+
+ /**
+ * Interface for the top level notification view that fades and optimizes that through deep
+ * awareness of individual components.
+ */
+ interface FadeOptimizedNotification extends NotificationFadeAware {
+ /** Top-level feature switch */
+ boolean FADE_LAYER_OPTIMIZATION_ENABLED = true;
+
+ /** Determine if the notification is currently faded. */
+ boolean isNotificationFaded();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt
new file mode 100644
index 0000000..1f2d0fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.statusbar.notification
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import javax.inject.Inject
+
+/**
+ * A class which is used to classify the sections.
+ * NOTE: This class exists to avoid putting metadata like "isMinimized" on the NotifSection
+ */
+@SysUISingleton
+class SectionClassifier @Inject constructor() {
+ private lateinit var lowPrioritySections: Set<NotifSectioner>
+
+ /**
+ * Feed the provider the information it needs about which sections should have minimized top
+ * level views, so that it can calculate the correct minimized state.
+ */
+ fun setMinimizedSections(sections: Collection<NotifSectioner>) {
+ lowPrioritySections = sections.toSet()
+ }
+
+ /**
+ * Determine if the given section is minimized
+ */
+ fun isMinimizedSection(section: NotifSection): Boolean {
+ return lowPrioritySections.contains(section.sectioner)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index 0ea6857..918843c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -19,8 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -61,24 +59,6 @@
mSummary = summary;
}
- /**
- * @see #getUntruncatedChildCount()
- */
- public void setUntruncatedChildCount(int childCount) {
- mUntruncatedChildCount = childCount;
- }
-
- /**
- * Get the untruncated number of children from the data model, including those that will not
- * have views bound. This includes children that {@link PreparationCoordinator} will filter out
- * entirely when they are beyond the last visible child.
- *
- * TODO: This should move to some shared class between the model and view hierarchy
- */
- public int getUntruncatedChildCount() {
- return mUntruncatedChildCount;
- }
-
void clearChildren() {
mChildren.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index 52c5c3e..6be8a49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -55,15 +55,26 @@
interactionTracker.hasUserInteractedWith(entry.getKey()));
if (entry instanceof GroupEntry) {
GroupEntry ge = (GroupEntry) entry;
+ NotificationEntry summary = ge.getSummary();
+ if (summary != null) {
+ dumpEntry(summary,
+ topEntryIndex + ":*",
+ childEntryIndent,
+ sb,
+ true,
+ includeRecordKeeping,
+ interactionTracker.hasUserInteractedWith(summary.getKey()));
+ }
List<NotificationEntry> children = ge.getChildren();
for (int childIndex = 0; childIndex < children.size(); childIndex++) {
- dumpEntry(children.get(childIndex),
+ NotificationEntry child = children.get(childIndex);
+ dumpEntry(child,
topEntryIndex + "." + childIndex,
childEntryIndent,
sb,
true,
includeRecordKeeping,
- interactionTracker.hasUserInteractedWith(entry.getKey()));
+ interactionTracker.hasUserInteractedWith(child.getKey()));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 2d6522e..f8f1279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -245,9 +245,15 @@
DismissedByUserStats stats = entriesToDismiss.get(i).second;
requireNonNull(stats);
- if (entry != mNotificationSet.get(entry.getKey())) {
+ NotificationEntry storedEntry = mNotificationSet.get(entry.getKey());
+ if (storedEntry == null) {
+ mLogger.logNonExistentNotifDismissed(entry.getKey());
+ continue;
+ }
+ if (entry != storedEntry) {
throw mEulogizer.record(
- new IllegalStateException("Invalid entry: " + entry.getKey()));
+ new IllegalStateException("Invalid entry: "
+ + "different stored and dismissed entries for " + entry.getKey()));
}
if (entry.getDismissState() == DISMISSED) {
@@ -489,6 +495,37 @@
}
}
+ /**
+ * Get the group summary entry
+ * @param group
+ * @return
+ */
+ @Nullable
+ public NotificationEntry getGroupSummary(String group) {
+ return mNotificationSet
+ .values()
+ .stream()
+ .filter(it -> Objects.equals(it.getSbn().getGroup(), group))
+ .filter(it -> it.getSbn().getNotification().isGroupSummary())
+ .findFirst().orElse(null);
+ }
+
+ /**
+ * Checks if the entry is the only child in the logical group
+ * @param entry
+ * @return
+ */
+ public boolean isOnlyChildInGroup(NotificationEntry entry) {
+ String group = entry.getSbn().getGroup();
+ return mNotificationSet.get(entry.getKey()) == entry
+ && mNotificationSet
+ .values()
+ .stream()
+ .filter(it -> Objects.equals(it.getSbn().getGroup(), group))
+ .filter(it -> !it.getSbn().getNotification().isGroupSummary())
+ .count() == 1;
+ }
+
private void applyRanking(@NonNull RankingMap rankingMap) {
for (NotificationEntry entry : mNotificationSet.values()) {
if (!isCanceled(entry)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 4f3c287..4daed77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -93,7 +93,7 @@
public void onAsyncInflationFinished(NotificationEntry entry) {
mNotifErrorManager.clearInflationError(entry);
if (callback != null) {
- callback.onInflationFinished(entry);
+ callback.onInflationFinished(entry, entry.getRowController());
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index 9ae9fe5..6fbed9a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.collection
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener
@@ -31,6 +34,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.render.RenderStageManager
import javax.inject.Inject
/**
@@ -65,11 +69,15 @@
* 9. Finalize filters are fired on each notification ([.addFinalizeFilter])
* 10. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
* 11. The list is handed off to the view layer to be rendered
+ * 12. OnAfterRenderListListeners are fired ([.addOnAfterRenderListListener])
+ * 13. OnAfterRenderGroupListeners are fired ([.addOnAfterRenderGroupListener])
+ * 13. OnAfterRenderEntryListeners are fired ([.addOnAfterRenderEntryListener])
*/
@SysUISingleton
class NotifPipeline @Inject constructor(
private val mNotifCollection: NotifCollection,
- private val mShadeListBuilder: ShadeListBuilder
+ private val mShadeListBuilder: ShadeListBuilder,
+ private val mRenderStageManager: RenderStageManager
) : CommonNotifCollection {
/**
* Returns the list of all known notifications, i.e. the notifications that are currently posted
@@ -206,6 +214,28 @@
}
/**
+ * Called at the end of the pipeline after the notif list has been handed off to the view layer.
+ */
+ fun addOnAfterRenderListListener(listener: OnAfterRenderListListener) {
+ mRenderStageManager.addOnAfterRenderListListener(listener)
+ }
+
+ /**
+ * Called at the end of the pipeline after a group has been handed off to the view layer.
+ */
+ fun addOnAfterRenderGroupListener(listener: OnAfterRenderGroupListener) {
+ mRenderStageManager.addOnAfterRenderGroupListener(listener)
+ }
+
+ /**
+ * Called at the end of the pipeline after an entry has been handed off to the view layer.
+ * This will be called for every top level entry, every group summary, and every group child.
+ */
+ fun addOnAfterRenderEntryListener(listener: OnAfterRenderEntryListener) {
+ mRenderStageManager.addOnAfterRenderEntryListener(listener)
+ }
+
+ /**
* Get an object which can be used to update a notification (internally to the pipeline)
* in response to a user action.
*
@@ -218,8 +248,8 @@
/**
* Returns a read-only view in to the current shade list, i.e. the list of notifications that
- * are currently present in the shade. If this method is called during pipeline execution it
- * will return the current state of the list, which will likely be only partially-generated.
+ * are currently present in the shade.
+ * @throws IllegalStateException if called during pipeline execution.
*/
val shadeList: List<ListEntry>
get() = mShadeListBuilder.shadeList
@@ -227,21 +257,20 @@
/**
* Constructs a flattened representation of the notification tree, where each group will have
* the summary (if present) followed by the children.
+ * @throws IllegalStateException if called during pipeline execution.
*/
fun getFlatShadeList(): List<NotificationEntry> = shadeList.flatMap { entry ->
when (entry) {
is NotificationEntry -> sequenceOf(entry)
- is GroupEntry -> (entry.summary?.let { sequenceOf(it) }.orEmpty() +
- entry.children)
+ is GroupEntry -> sequenceOf(entry.summary).filterNotNull() + entry.children
else -> throw RuntimeException("Unexpected entry $entry")
}
}
/**
* Returns the number of notifications currently shown in the shade. This includes all
- * children and summary notifications. If this method is called during pipeline execution it
- * will return the number of notifications in its current state, which will likely be only
- * partially-generated.
+ * children and summary notifications.
+ * @throws IllegalStateException if called during pipeline execution.
*/
fun getShadeListCount(): Int = shadeList.sumOf { entry ->
// include the summary in the count
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 72cd951..748c69d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -42,6 +42,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.NotificationInteractionTracker;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
@@ -87,6 +88,7 @@
private final NotificationInteractionTracker mInteractionTracker;
// used exclusivly by ShadeListBuilder#notifySectionEntriesUpdated
private final ArrayList<ListEntry> mTempSectionMembers = new ArrayList<>();
+ private final boolean mAlwaysLogList;
private List<ListEntry> mNotifList = new ArrayList<>();
private List<ListEntry> mNewNotifList = new ArrayList<>();
@@ -119,6 +121,7 @@
@Inject
public ShadeListBuilder(
SystemClock systemClock,
+ NotifPipelineFlags flags,
ShadeListBuilderLogger logger,
DumpManager dumpManager,
NotificationInteractionTracker interactionTracker
@@ -126,6 +129,7 @@
Assert.isMainThread();
mSystemClock = systemClock;
mLogger = logger;
+ mAlwaysLogList = flags.isDevLoggingEnabled();
mInteractionTracker = interactionTracker;
dumpManager.registerDumpable(TAG, this);
@@ -253,6 +257,9 @@
List<ListEntry> getShadeList() {
Assert.isMainThread();
+ // NOTE: Accessing this method when the pipeline is running is generally going to provide
+ // incorrect results, and indicates a poorly behaved component of the pipeline.
+ mPipelineState.requireState(STATE_IDLE);
return mReadOnlyNotifList;
}
@@ -404,7 +411,7 @@
mIterationCount,
mReadOnlyNotifList.size(),
countChildren(mReadOnlyNotifList));
- if (mIterationCount % 10 == 0) {
+ if (mAlwaysLogList || mIterationCount % 10 == 0) {
mLogger.logFinalList(mNotifList);
}
mPipelineState.setState(STATE_IDLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index d013261..e9b7caa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -57,6 +58,7 @@
public class BubbleCoordinator implements Coordinator {
private static final String TAG = "BubbleCoordinator";
+ private final NotifPipelineFlags mNotifPipelineFlags;
private final Optional<BubblesManager> mBubblesManagerOptional;
private final Optional<Bubbles> mBubblesOptional;
private final NotifCollection mNotifCollection;
@@ -66,9 +68,11 @@
@Inject
public BubbleCoordinator(
+ NotifPipelineFlags notifPipelineFlags,
Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
NotifCollection notifCollection) {
+ mNotifPipelineFlags = notifPipelineFlags;
mBubblesManagerOptional = bubblesManagerOptional;
mBubblesOptional = bubblesOptional;
mNotifCollection = notifCollection;
@@ -130,6 +134,14 @@
DismissedByUserStats dismissedByUserStats,
int reason
) {
+ if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
+ // The `entry` will be from whichever pipeline is active, so if the old pipeline is
+ // running, make sure that we use the new pipeline's entry (if it still exists).
+ NotificationEntry newPipelineEntry = mNotifPipeline.getEntry(entry.getKey());
+ if (newPipelineEntry != null) {
+ entry = newPipelineEntry;
+ }
+ }
if (isInterceptingDismissal(entry)) {
mInterceptedDismissalEntries.remove(entry.getKey());
mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
index 992d898..bd011c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
@@ -19,6 +19,7 @@
import androidx.annotation.NonNull;
import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -26,6 +27,8 @@
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import java.util.concurrent.Executor;
+
import javax.inject.Inject;
/**
@@ -34,14 +37,17 @@
*/
@CoordinatorScope
public class CommunalCoordinator implements Coordinator {
+ final Executor mExecutor;
final CommunalStateController mCommunalStateController;
final NotificationEntryManager mNotificationEntryManager;
final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Inject
- public CommunalCoordinator(NotificationEntryManager notificationEntryManager,
+ public CommunalCoordinator(@Main Executor executor,
+ NotificationEntryManager notificationEntryManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
CommunalStateController communalStateController) {
+ mExecutor = executor;
mNotificationEntryManager = notificationEntryManager;
mNotificationLockscreenUserManager = notificationLockscreenUserManager;
mCommunalStateController = communalStateController;
@@ -57,8 +63,10 @@
final CommunalStateController.Callback mStateCallback = new CommunalStateController.Callback() {
@Override
public void onCommunalViewShowingChanged() {
- mFilter.invalidateList();
- mNotificationEntryManager.updateNotifications("Communal mode state changed");
+ mExecutor.execute(() -> {
+ mFilter.invalidateList();
+ mNotificationEntryManager.updateNotifications("Communal mode state changed");
+ });
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
new file mode 100644
index 0000000..82b1268
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
@@ -0,0 +1,35 @@
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.util.ArrayMap
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.render.NotifGroupController
+import javax.inject.Inject
+
+/** A small coordinator which calculates, stores, and applies the untruncated child count. */
+@CoordinatorScope
+class GroupCountCoordinator @Inject constructor() : Coordinator {
+ private val untruncatedChildCounts = ArrayMap<GroupEntry, Int>()
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilter)
+ pipeline.addOnAfterRenderGroupListener(::onAfterRenderGroup)
+ }
+
+ private fun onBeforeFinalizeFilter(entries: List<ListEntry>) {
+ // save untruncated child counts to our internal map
+ untruncatedChildCounts.clear()
+ entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
+ untruncatedChildCounts[groupEntry] = groupEntry.children.size
+ }
+ }
+
+ private fun onAfterRenderGroup(group: GroupEntry, controller: NotifGroupController) {
+ // find the untruncated child count for a group and apply it to the controller
+ val count = untruncatedChildCounts[group]
+ checkNotNull(count) { "No untruncated child count for group: ${group.key}" }
+ controller.setUntruncatedChildCount(count)
+ }
+}
\ No newline at end of file
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 dae76f8..a16b565 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
@@ -46,8 +46,11 @@
gutsCoordinator: GutsCoordinator,
conversationCoordinator: ConversationCoordinator,
preparationCoordinator: PreparationCoordinator,
+ groupCountCoordinator: GroupCountCoordinator,
mediaCoordinator: MediaCoordinator,
remoteInputCoordinator: RemoteInputCoordinator,
+ rowAppearanceCoordinator: RowAppearanceCoordinator,
+ stackCoordinator: StackCoordinator,
shadeEventCoordinator: ShadeEventCoordinator,
smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
viewConfigCoordinator: ViewConfigCoordinator,
@@ -72,8 +75,11 @@
mCoordinators.add(deviceProvisionedCoordinator)
mCoordinators.add(bubbleCoordinator)
mCoordinators.add(conversationCoordinator)
+ mCoordinators.add(groupCountCoordinator)
mCoordinators.add(mediaCoordinator)
mCoordinators.add(remoteInputCoordinator)
+ mCoordinators.add(rowAppearanceCoordinator)
+ mCoordinators.add(stackCoordinator)
mCoordinators.add(shadeEventCoordinator)
mCoordinators.add(viewConfigCoordinator)
mCoordinators.add(visualStabilityCoordinator)
@@ -89,7 +95,7 @@
}
// Manually add Ordered Sections
- // HeadsUp > FGS > People > Alerting > Silent > Unknown/Default
+ // HeadsUp > FGS > People > Alerting > Silent > Minimized > Unknown/Default
if (notifPipelineFlags.isNewPipelineEnabled()) {
mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 644f248..bbb97d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -31,19 +31,19 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustment;
import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
-import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn;
+import com.android.systemui.statusbar.notification.collection.render.NotifViewController;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager.NotifInflationErrorListener;
@@ -61,8 +61,7 @@
* If a notification was uninflated, this coordinator will filter the notification out from the
* {@link ShadeListBuilder} until it is inflated.
*/
-// TODO(b/204468557): Move to @CoordinatorScope
-@SysUISingleton
+@CoordinatorScope
public class PreparationCoordinator implements Coordinator {
private static final String TAG = "PreparationCoordinator";
@@ -145,7 +144,7 @@
pipeline.addCollectionListener(mNotifCollectionListener);
// Inflate after grouping/sorting since that affects what views to inflate.
- pipeline.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener);
+ pipeline.addOnBeforeFinalizeFilterListener(this::inflateAllRequiredViews);
pipeline.addFinalizeFilter(mNotifInflationErrorFilter);
pipeline.addFinalizeFilter(mNotifInflatingFilter);
}
@@ -182,9 +181,6 @@
}
};
- private final OnBeforeFinalizeFilterListener mOnBeforeFinalizeFilterListener =
- entries -> inflateAllRequiredViews(entries);
-
private final NotifFilter mNotifInflationErrorFilter = new NotifFilter(
TAG + "InflationError") {
/**
@@ -256,7 +252,6 @@
ListEntry entry = entries.get(i);
if (entry instanceof GroupEntry) {
GroupEntry groupEntry = (GroupEntry) entry;
- groupEntry.setUntruncatedChildCount(groupEntry.getChildren().size());
inflateRequiredGroupViews(groupEntry);
} else {
NotificationEntry notifEntry = (NotificationEntry) entry;
@@ -363,17 +358,17 @@
mInflatingNotifs.remove(entry);
}
- private void onInflationFinished(NotificationEntry entry) {
+ private void onInflationFinished(NotificationEntry entry, NotifViewController controller) {
mLogger.logNotifInflated(entry.getKey());
mInflatingNotifs.remove(entry);
- mViewBarn.registerViewForEntry(entry, entry.getRowController());
+ mViewBarn.registerViewForEntry(entry, controller);
mInflationStates.put(entry, STATE_INFLATED);
mNotifInflatingFilter.invalidateList();
}
private void freeNotifViews(NotificationEntry entry) {
mViewBarn.removeViewForEntry(entry);
- entry.setRow(null);
+ // TODO: clear the entry's row here, or even better, stop setting the row on the entry!
mInflationStates.put(entry, STATE_UNINFLATED);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index c60ebcd..57fd197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -20,11 +20,11 @@
import android.annotation.Nullable;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.SectionClassifier;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
-import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -51,7 +51,7 @@
public static final boolean SHOW_ALL_SECTIONS = false;
private final StatusBarStateController mStatusBarStateController;
private final HighPriorityProvider mHighPriorityProvider;
- private final NotifUiAdjustmentProvider mAdjustmentProvider;
+ private final SectionClassifier mSectionClassifier;
private final NodeController mSilentNodeController;
private final SectionHeaderController mSilentHeaderController;
private final NodeController mAlertingHeaderController;
@@ -62,13 +62,13 @@
public RankingCoordinator(
StatusBarStateController statusBarStateController,
HighPriorityProvider highPriorityProvider,
- NotifUiAdjustmentProvider adjustmentProvider,
+ SectionClassifier sectionClassifier,
@AlertingHeader NodeController alertingHeaderController,
@SilentHeader SectionHeaderController silentHeaderController,
@SilentHeader NodeController silentNodeController) {
mStatusBarStateController = statusBarStateController;
mHighPriorityProvider = highPriorityProvider;
- mAdjustmentProvider = adjustmentProvider;
+ mSectionClassifier = sectionClassifier;
mAlertingHeaderController = alertingHeaderController;
mSilentNodeController = silentNodeController;
mSilentHeaderController = silentHeaderController;
@@ -77,7 +77,7 @@
@Override
public void attach(NotifPipeline pipeline) {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
- mAdjustmentProvider.setLowPrioritySections(Collections.singleton(mMinimizedNotifSectioner));
+ mSectionClassifier.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner));
pipeline.addPreGroupFilter(mSuspendedFilter);
pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
index 3397815..2608c30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
@@ -22,7 +22,6 @@
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.NotificationRemoteInputManager
@@ -32,6 +31,7 @@
import com.android.systemui.statusbar.SmartReplyController
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.notifcollection.SelfTrackingLifetimeExtender
import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -61,7 +61,7 @@
/** Whether this class should print spammy debug logs */
private val DEBUG: Boolean by lazy { Log.isLoggable(TAG, Log.DEBUG) }
-@SysUISingleton
+@CoordinatorScope
class RemoteInputCoordinator @Inject constructor(
dumpManager: DumpManager,
private val mRebuilder: RemoteInputNotificationRebuilder,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
new file mode 100644
index 0000000..4e9d3ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.statusbar.notification.collection.coordinator
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.AssistantFeedbackController
+import com.android.systemui.statusbar.notification.SectionClassifier
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.render.NotifRowController
+import javax.inject.Inject
+
+/**
+ * A small coordinator which updates the notif rows with data related to the current shade after
+ * they are fully attached.
+ */
+@CoordinatorScope
+class RowAppearanceCoordinator @Inject internal constructor(
+ context: Context,
+ private var mAssistantFeedbackController: AssistantFeedbackController,
+ private var mSectionClassifier: SectionClassifier
+) : Coordinator {
+
+ private var entryToExpand: NotificationEntry? = null
+
+ /**
+ * `true` if notifications not part of a group should by default be rendered in their
+ * expanded state. If `false`, then only the first notification will be expanded if
+ * possible.
+ */
+ private val mAlwaysExpandNonGroupedNotification =
+ context.resources.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications)
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addOnBeforeRenderListListener(::onBeforeRenderList)
+ pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
+ }
+
+ private fun onBeforeRenderList(list: List<ListEntry>) {
+ entryToExpand = list.firstOrNull()?.representativeEntry?.takeIf { entry ->
+ !mSectionClassifier.isMinimizedSection(entry.section!!)
+ }
+ }
+
+ private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) {
+ // If mAlwaysExpandNonGroupedNotification is false, then only expand the
+ // very first notification and if it's not a child of grouped notifications.
+ controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification || entry == entryToExpand)
+ // Show/hide the feedback icon
+ controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry))
+ // Show the "alerted" bell icon
+ controller.setLastAudiblyAlertedMs(entry.lastAudiblyAlertedMs)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
new file mode 100644
index 0000000..38f11fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.statusbar.notification.collection.coordinator
+
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController
+import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
+import com.android.systemui.statusbar.phone.NotificationIconAreaController
+import javax.inject.Inject
+
+/**
+ * A small coordinator which updates the notif stack (the view layer which holds notifications)
+ * with high-level data after the stack is populated with the final entries.
+ */
+@CoordinatorScope
+class StackCoordinator @Inject internal constructor(
+ private val notificationIconAreaController: NotificationIconAreaController
+) : Coordinator {
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addOnAfterRenderListListener(::onAfterRenderList)
+ }
+
+ fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) {
+ controller.setNotifStats(calculateNotifStats(entries))
+ notificationIconAreaController.updateNotificationIcons(entries)
+ }
+
+ private fun calculateNotifStats(entries: List<ListEntry>): NotifStats {
+ var hasNonClearableAlertingNotifs = false
+ var hasClearableAlertingNotifs = false
+ var hasNonClearableSilentNotifs = false
+ var hasClearableSilentNotifs = false
+ entries.forEach {
+ val isSilent = it.section!!.bucket == BUCKET_SILENT
+ // NOTE: NotificationEntry.isClearable will internally check group children to ensure
+ // the group itself definitively clearable.
+ val isClearable = it.representativeEntry!!.isClearable
+ when {
+ isSilent && isClearable -> hasClearableSilentNotifs = true
+ isSilent && !isClearable -> hasNonClearableSilentNotifs = true
+ !isSilent && isClearable -> hasClearableAlertingNotifs = true
+ !isSilent && !isClearable -> hasNonClearableAlertingNotifs = true
+ }
+ }
+ val stats = NotifStats(
+ numActiveNotifs = entries.size,
+ hasNonClearableAlertingNotifs = hasNonClearableAlertingNotifs,
+ hasClearableAlertingNotifs = hasClearableAlertingNotifs,
+ hasNonClearableSilentNotifs = hasNonClearableSilentNotifs,
+ hasClearableSilentNotifs = hasClearableSilentNotifs
+ )
+ return stats
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
index c59f184..d98e7f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.inflation
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.render.NotifViewController
/**
* Used by the [PreparationCoordinator]. When notifications are added or updated, the
@@ -49,7 +50,7 @@
* Callback once all the views are inflated and bound for a given NotificationEntry.
*/
interface InflationCallback {
- fun onInflationFinished(entry: NotificationEntry)
+ fun onInflationFinished(entry: NotificationEntry, controller: NotifViewController)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 3290cdf..497691d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -17,9 +17,9 @@
package com.android.systemui.statusbar.notification.collection.inflation
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.SectionClassifier
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import javax.inject.Inject
/**
@@ -27,25 +27,17 @@
* to ensure that notifications are reinflated when ranking-derived information changes.
*/
@SysUISingleton
-open class NotifUiAdjustmentProvider @Inject constructor() {
-
- private lateinit var lowPrioritySections: Set<NotifSectioner>
-
- /**
- * Feed the provider the information it needs about which sections should have minimized top
- * level views, so that it can calculate the correct minimized value in the adjustment.
- */
- fun setLowPrioritySections(sections: Collection<NotifSectioner>) {
- lowPrioritySections = sections.toSet()
- }
+open class NotifUiAdjustmentProvider @Inject constructor(
+ private val sectionClassifier: SectionClassifier
+) {
private fun isEntryMinimized(entry: NotificationEntry): Boolean {
val section = entry.section ?: error("Entry must have a section to determine if minimized")
val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
- val isLowPrioritySection = lowPrioritySections.contains(section.sectioner)
+ val isMinimizedSection = sectionClassifier.isMinimizedSection(section)
val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
val isGroupSummary = parent.summary == entry
- return isLowPrioritySection && (isTopLevelEntry || isGroupSummary)
+ return isMinimizedSection && (isTopLevelEntry || isGroupSummary)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 66ec30d..f50038c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -108,11 +108,10 @@
@Override
@Nullable
public NotificationEntry getGroupSummaryToDismiss(NotificationEntry entry) {
- if (entry.getParent() != null
- && entry.getParent().getSummary() != null
- && mGroupMembershipManager.isOnlyChildInGroup(entry)) {
- NotificationEntry groupSummary = entry.getParent().getSummary();
- return groupSummary.isClearable() ? groupSummary : null;
+ String group = entry.getSbn().getGroup();
+ if (mNotifCollection.isOnlyChildInGroup(entry)) {
+ NotificationEntry summary = mNotifCollection.getGroupSummary(group);
+ if (summary != null && summary.isClearable()) return summary;
}
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index a8f3730..f9358eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -30,6 +30,8 @@
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
+import com.android.systemui.statusbar.notification.collection.render.RenderStageManager;
import com.android.systemui.statusbar.notification.collection.render.ShadeViewManagerFactory;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -47,6 +49,7 @@
private final GroupCoalescer mGroupCoalescer;
private final NotifCollection mNotifCollection;
private final ShadeListBuilder mListBuilder;
+ private final RenderStageManager mRenderStageManager;
private final NotifCoordinators mNotifPluggableCoordinators;
private final NotifInflaterImpl mNotifInflater;
private final DumpManager mDumpManager;
@@ -60,15 +63,18 @@
GroupCoalescer groupCoalescer,
NotifCollection notifCollection,
ShadeListBuilder listBuilder,
+ RenderStageManager renderStageManager,
NotifCoordinators notifCoordinators,
NotifInflaterImpl notifInflater,
DumpManager dumpManager,
ShadeViewManagerFactory shadeViewManagerFactory,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags
+ ) {
mPipelineWrapper = pipelineWrapper;
mGroupCoalescer = groupCoalescer;
mNotifCollection = notifCollection;
mListBuilder = listBuilder;
+ mRenderStageManager = renderStageManager;
mNotifPluggableCoordinators = notifCoordinators;
mDumpManager = dumpManager;
mNotifInflater = notifInflater;
@@ -80,7 +86,8 @@
public void initialize(
NotificationListener notificationService,
NotificationRowBinderImpl rowBinder,
- NotificationListContainer listContainer) {
+ NotificationListContainer listContainer,
+ NotifStackController stackController) {
mDumpManager.registerDumpable("NotifPipeline", this);
@@ -94,8 +101,11 @@
// Wire up pipeline
if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- mShadeViewManagerFactory.create(listContainer).attach(mListBuilder);
+ mShadeViewManagerFactory
+ .create(listContainer, stackController)
+ .attach(mRenderStageManager);
}
+ mRenderStageManager.attach(mListBuilder);
mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
mGroupCoalescer.attach(notificationService);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderEntryListener.java
new file mode 100644
index 0000000..20cd6dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderEntryListener.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.NotifRowController;
+
+/** See {@link NotifPipeline#addOnAfterRenderEntryListener(OnAfterRenderEntryListener)} */
+public interface OnAfterRenderEntryListener {
+ /**
+ * Called at the end of the pipeline after an entry has been handed off to the view layer.
+ * This will be called for every top level entry, every group summary, and every group child.
+ *
+ * @param entry the entry to read from.
+ * @param controller the object to which data can be pushed.
+ */
+ void onAfterRenderEntry(
+ @NonNull NotificationEntry entry,
+ @NonNull NotifRowController controller);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderGroupListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderGroupListener.java
new file mode 100644
index 0000000..b1a4b65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderGroupListener.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.render.NotifGroupController;
+
+/** See {@link NotifPipeline#addOnAfterRenderGroupListener(OnAfterRenderGroupListener)} */
+public interface OnAfterRenderGroupListener {
+ /**
+ * Called at the end of the pipeline after a group has been handed off to the view layer.
+ *
+ * @param groupEntry the entry for the group itself.
+ * @param controller the object to which data can be pushed.
+ */
+ void onAfterRenderGroup(
+ @NonNull GroupEntry groupEntry,
+ @NonNull NotifGroupController controller);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
new file mode 100644
index 0000000..b5a0f7a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
+
+import java.util.List;
+
+/** See {@link NotifPipeline#addOnAfterRenderListListener(OnAfterRenderListListener)} */
+public interface OnAfterRenderListListener {
+ /**
+ * Called at the end of the pipeline after the notif list has been handed off to the view layer.
+ *
+ * @param entries The current list of top-level entries. Note that this is a live view into the
+ * current list and will change whenever the pipeline is rerun.
+ * @param controller An object for setting state on the shade.
+ */
+ void onAfterRenderList(
+ @NonNull List<ListEntry> entries,
+ @NonNull NotifStackController controller);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index b4389b9..7302de5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -109,6 +109,14 @@
})
}
+ fun logNonExistentNotifDismissed(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "DISMISSED Non Existent $str1"
+ })
+ }
+
fun logChildDismissed(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, {
str1 = entry.key
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index b9aa26f..86d263a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -25,6 +25,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -91,7 +92,7 @@
@Override
public void collapseGroups() {
- for (NotificationEntry entry : mExpandedGroups) {
+ for (NotificationEntry entry : new ArrayList<>(mExpandedGroups)) {
setGroupExpanded(entry, false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index 8182e73..f59e4ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -64,12 +64,13 @@
root.children.add(buildNotifNode(root, entry))
}
- return root
+ return@traceSection root
}
private fun buildNotifNode(parent: NodeSpec, entry: ListEntry): NodeSpec = when (entry) {
- is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireView(entry))
- is GroupEntry -> NodeSpecImpl(parent, viewBarn.requireView(checkNotNull(entry.summary)))
+ is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireNodeController(entry))
+ is GroupEntry ->
+ NodeSpecImpl(parent, viewBarn.requireNodeController(checkNotNull(entry.summary)))
.apply { entry.children.forEach { children.add(buildNotifNode(this, it)) } }
else -> throw RuntimeException("Unexpected entry: $entry")
}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
similarity index 65%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
index 3729c09..e2edc01 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.window;
+package com.android.systemui.statusbar.notification.collection.render
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+/** A view controller for a notification group row */
+interface NotifGroupController {
+ /** Set the number of children that this group would have if not for the 8-child max */
+ fun setUntruncatedChildCount(untruncatedChildCount: Int)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt
new file mode 100644
index 0000000..5ee94ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+import com.android.systemui.statusbar.notification.FeedbackIcon
+
+/** A view controller for a notification row */
+interface NotifRowController {
+ /**
+ * This tells the row what the 'default expanded' state should be. Once a user expands or
+ * contracts a row, that will set the user expanded state, which takes precedence, but
+ * collapsing the shade and re-opening it will clear the user expanded state. This allows for
+ * nice auto expansion of the next notification as users dismiss the top notification.
+ */
+ fun setSystemExpanded(systemExpanded: Boolean)
+
+ /**
+ * Sets the timestamp that the notification was last audibly alerted, which the row uses to
+ * show a bell icon in the header which indicates to the user which notification made a noise.
+ */
+ fun setLastAudiblyAlertedMs(lastAudiblyAlertedMs: Long)
+
+ /** Shows the given feedback icon, or hides the icon if null. */
+ fun setFeedbackIcon(icon: FeedbackIcon?)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
new file mode 100644
index 0000000..b6278d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+/** An interface by which the pipeline can make updates to the notification root view. */
+interface NotifStackController {
+ /** Provides stats about the list of notifications attached to the shade */
+ fun setNotifStats(stats: NotifStats)
+}
+
+/** Data provided to the NotificationRootController whenever the pipeline runs */
+data class NotifStats(
+ val numActiveNotifs: Int,
+ val hasNonClearableAlertingNotifs: Boolean,
+ val hasClearableAlertingNotifs: Boolean,
+ val hasNonClearableSilentNotifs: Boolean,
+ val hasClearableSilentNotifs: Boolean
+) {
+ companion object {
+ @JvmStatic
+ val empty = NotifStats(0, false, false, false, false)
+ }
+}
+
+/**
+ * An implementation of NotifStackController which provides default, no-op implementations of each
+ * method. This is used by ArcSystemUI so that that implementation can opt-in to overriding
+ * methods, rather than forcing us to add no-op implementations in their implementation every time
+ * a method is added.
+ */
+open class DefaultNotifStackController : NotifStackController {
+ override fun setNotifStats(stats: NotifStats) {}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
index c79f59b..fd91d5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
@@ -19,6 +19,7 @@
import android.view.textclassifier.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
/**
@@ -26,30 +27,39 @@
*/
@SysUISingleton
class NotifViewBarn @Inject constructor() {
- private val rowMap = mutableMapOf<String, NodeController>()
+ private val rowMap = mutableMapOf<String, NotifViewController>()
- fun requireView(forEntry: ListEntry): NodeController {
+ fun requireNodeController(entry: ListEntry): NodeController {
if (DEBUG) {
- Log.d(TAG, "requireView: $forEntry.key")
+ Log.d(TAG, "requireNodeController: ${entry.key}")
}
- val li = rowMap[forEntry.key]
- if (li == null) {
- throw IllegalStateException("No view has been registered for entry: $forEntry")
- }
-
- return li
+ return rowMap[entry.key] ?: error("No view has been registered for entry: ${entry.key}")
}
- fun registerViewForEntry(entry: ListEntry, controller: NodeController) {
+ fun requireGroupController(entry: NotificationEntry): NotifGroupController {
if (DEBUG) {
- Log.d(TAG, "registerViewForEntry: $entry.key")
+ Log.d(TAG, "requireGroupController: ${entry.key}")
+ }
+ return rowMap[entry.key] ?: error("No view has been registered for entry: ${entry.key}")
+ }
+
+ fun requireRowController(entry: NotificationEntry): NotifRowController {
+ if (DEBUG) {
+ Log.d(TAG, "requireRowController: ${entry.key}")
+ }
+ return rowMap[entry.key] ?: error("No view has been registered for entry: ${entry.key}")
+ }
+
+ fun registerViewForEntry(entry: ListEntry, controller: NotifViewController) {
+ if (DEBUG) {
+ Log.d(TAG, "registerViewForEntry: ${entry.key}")
}
rowMap[entry.key] = controller
}
fun removeViewForEntry(entry: ListEntry) {
if (DEBUG) {
- Log.d(TAG, "removeViewForEntry: $entry.key")
+ Log.d(TAG, "removeViewForEntry: ${entry.key}")
}
rowMap.remove(entry.key)
}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewController.kt
similarity index 76%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewController.kt
index 3729c09..11d4e83 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewController.kt
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package android.window;
+package com.android.systemui.statusbar.notification.collection.render
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+interface NotifViewController : NotifGroupController, NotifRowController, NodeController
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
new file mode 100644
index 0000000..1ea574b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * This interface and the interfaces it returns define the main API surface that must be
+ * implemented by the view implementation. The term "render" is used to indicate a handoff
+ * to the view system, whether that be to attach views to the hierarchy or to update independent
+ * view models, data stores, or adapters.
+ */
+interface NotifViewRenderer {
+
+ /**
+ * Hand off the list of notifications to the view implementation. This may attach views to the
+ * hierarchy or simply update an independent datastore, but once called, the implementer myst
+ * also ensure that future calls to [getStackController], [getGroupController], and
+ * [getRowController] will provide valid results.
+ */
+ fun onRenderList(notifList: List<ListEntry>)
+
+ /**
+ * Provides an interface for the pipeline to update the overall shade.
+ * This will be called at most once for each time [onRenderList] is called.
+ */
+ fun getStackController(): NotifStackController
+
+ /**
+ * Provides an interface for the pipeline to update individual groups.
+ * This will be called at most once for each group in the most recent call to [onRenderList].
+ */
+ fun getGroupController(group: GroupEntry): NotifGroupController
+
+ /**
+ * Provides an interface for the pipeline to update individual entries.
+ * This will be called at most once for each entry in the most recent call to [onRenderList].
+ * This includes top level entries, group summaries, and group children.
+ */
+ fun getRowController(entry: NotificationEntry): NotifRowController
+
+ /**
+ * Invoked after the render stage manager has finished dispatching to all of the listeners.
+ *
+ * This is an opportunity for the view system to do any cleanup or trigger any finalization
+ * logic now that all data from the pipeline is known to have been set for this execution.
+ *
+ * When this is called, the view system can expect that no more calls will be made to the
+ * getters on this interface until after the next call to [onRenderList]. Additionally, there
+ * should be no further calls made on the objects previously returned by those getters.
+ */
+ fun onDispatchComplete() {}
+}
\ No newline at end of file
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderExtensions.kt
similarity index 63%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderExtensions.kt
index 3729c09..6e7f415 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderExtensions.kt
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.window;
+package com.android.systemui.statusbar.notification.collection.render
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry
/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
+ * Extension used during the render stage which assumes the summary exists, and throws a more
+ * helpful error if not.
*/
-parcelable TaskFragmentAppearedInfo;
+inline val GroupEntry.requireSummary get() = checkNotNull(summary) { "No Summary: $this" }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
new file mode 100644
index 0000000..a9c3987
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
+import com.android.systemui.util.traceSection
+import javax.inject.Inject
+
+/**
+ * The class which is part of the pipeline which guarantees a consistent that coordinators get a
+ * consistent interface to the view system regardless of the [NotifViewRenderer] implementation
+ * provided to [setViewRenderer].
+ */
+@SysUISingleton
+class RenderStageManager @Inject constructor() {
+ private val onAfterRenderListListeners = mutableListOf<OnAfterRenderListListener>()
+ private val onAfterRenderGroupListeners = mutableListOf<OnAfterRenderGroupListener>()
+ private val onAfterRenderEntryListeners = mutableListOf<OnAfterRenderEntryListener>()
+ private var viewRenderer: NotifViewRenderer? = null
+
+ /** Attach this stage to the rest of the pipeline */
+ fun attach(listBuilder: ShadeListBuilder) {
+ listBuilder.setOnRenderListListener(::onRenderList)
+ }
+
+ private fun onRenderList(notifList: List<ListEntry>) {
+ traceSection("RenderStageManager.onRenderList") {
+ val viewRenderer = viewRenderer ?: return
+ viewRenderer.onRenderList(notifList)
+ dispatchOnAfterRenderList(viewRenderer, notifList)
+ dispatchOnAfterRenderGroups(viewRenderer, notifList)
+ dispatchOnAfterRenderEntries(viewRenderer, notifList)
+ viewRenderer.onDispatchComplete()
+ }
+ }
+
+ /** Provides this class with the view rendering implementation. */
+ fun setViewRenderer(renderer: NotifViewRenderer) {
+ viewRenderer = renderer
+ }
+
+ /** Adds a listener that will get a single callback after rendering the list. */
+ fun addOnAfterRenderListListener(listener: OnAfterRenderListListener) {
+ onAfterRenderListListeners.add(listener)
+ }
+
+ /** Adds a listener that will get a callback for each group rendered. */
+ fun addOnAfterRenderGroupListener(listener: OnAfterRenderGroupListener) {
+ onAfterRenderGroupListeners.add(listener)
+ }
+
+ /** Adds a listener that will get a callback for each entry rendered. */
+ fun addOnAfterRenderEntryListener(listener: OnAfterRenderEntryListener) {
+ onAfterRenderEntryListeners.add(listener)
+ }
+
+ private fun dispatchOnAfterRenderList(
+ viewRenderer: NotifViewRenderer,
+ entries: List<ListEntry>
+ ) {
+ traceSection("RenderStageManager.dispatchOnAfterRenderList") {
+ val stackController = viewRenderer.getStackController()
+ onAfterRenderListListeners.forEach { listener ->
+ listener.onAfterRenderList(entries, stackController)
+ }
+ }
+ }
+
+ private fun dispatchOnAfterRenderGroups(
+ viewRenderer: NotifViewRenderer,
+ entries: List<ListEntry>
+ ) {
+ traceSection("RenderStageManager.dispatchOnAfterRenderGroups") {
+ if (onAfterRenderGroupListeners.isEmpty()) {
+ return
+ }
+ entries.asSequence().filterIsInstance<GroupEntry>().forEach { group ->
+ val controller = viewRenderer.getGroupController(group)
+ onAfterRenderGroupListeners.forEach { listener ->
+ listener.onAfterRenderGroup(group, controller)
+ }
+ }
+ }
+ }
+
+ private fun dispatchOnAfterRenderEntries(
+ viewRenderer: NotifViewRenderer,
+ entries: List<ListEntry>
+ ) {
+ traceSection("RenderStageManager.dispatchOnAfterRenderEntries") {
+ if (onAfterRenderEntryListeners.isEmpty()) {
+ return
+ }
+ entries.forEachNotificationEntry { entry ->
+ val controller = viewRenderer.getRowController(entry)
+ onAfterRenderEntryListeners.forEach { listener ->
+ listener.onAfterRenderEntry(entry, controller)
+ }
+ }
+ }
+ }
+
+ /**
+ * Performs a forward, depth-first traversal of the list where the group's summary
+ * immediately precedes the group's children.
+ */
+ private inline fun List<ListEntry>.forEachNotificationEntry(
+ action: (NotificationEntry) -> Unit
+ ) {
+ forEach { entry ->
+ when (entry) {
+ is NotificationEntry -> action(entry)
+ is GroupEntry -> {
+ action(entry.requireSummary)
+ entry.children.forEach(action)
+ }
+ else -> error("Unhandled entry: $entry")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index b582a24..1a8d720 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -20,10 +20,8 @@
import android.view.View
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
-import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.util.traceSection
import javax.inject.Inject
@@ -34,9 +32,9 @@
class ShadeViewManager constructor(
context: Context,
listContainer: NotificationListContainer,
+ private val stackController: NotifStackController,
logger: ShadeViewDifferLogger,
- private val viewBarn: NotifViewBarn,
- private val notificationIconAreaController: NotificationIconAreaController
+ private val viewBarn: NotifViewBarn
) {
// We pass a shim view here because the listContainer may not actually have a view associated
// with it and the differ never actually cares about the root node's view.
@@ -44,39 +42,39 @@
private val specBuilder = NodeSpecBuilder(viewBarn)
private val viewDiffer = ShadeViewDiffer(rootController, logger)
- fun attach(listBuilder: ShadeListBuilder) =
- listBuilder.setOnRenderListListener(::onNewNotifTree)
-
- private fun onNewNotifTree(notifList: List<ListEntry>) {
- traceSection("ShadeViewManager.onNewNotifTree") {
- viewDiffer.applySpec(specBuilder.buildNodeSpec(rootController, notifList))
- updateGroupCounts(notifList)
- notificationIconAreaController.updateNotificationIcons(notifList)
- }
+ /** Method for attaching this manager to the pipeline. */
+ fun attach(renderStageManager: RenderStageManager) {
+ renderStageManager.setViewRenderer(viewRenderer)
}
- private fun updateGroupCounts(notifList: List<ListEntry>) {
- traceSection("ShadeViewManager.updateGroupCounts") {
- notifList.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
- val controller = viewBarn.requireView(checkNotNull(groupEntry.summary))
- val row = controller.view as ExpandableNotificationRow
- row.setUntruncatedChildCount(groupEntry.untruncatedChildCount)
+ private val viewRenderer = object : NotifViewRenderer {
+
+ override fun onRenderList(notifList: List<ListEntry>) {
+ traceSection("ShadeViewManager.onRenderList") {
+ viewDiffer.applySpec(specBuilder.buildNodeSpec(rootController, notifList))
}
}
+
+ override fun getStackController(): NotifStackController = stackController
+
+ override fun getGroupController(group: GroupEntry): NotifGroupController =
+ viewBarn.requireGroupController(group.requireSummary)
+
+ override fun getRowController(entry: NotificationEntry): NotifRowController =
+ viewBarn.requireRowController(entry)
}
}
class ShadeViewManagerFactory @Inject constructor(
private val context: Context,
private val logger: ShadeViewDifferLogger,
- private val viewBarn: NotifViewBarn,
- private val notificationIconAreaController: NotificationIconAreaController
+ private val viewBarn: NotifViewBarn
) {
- fun create(listContainer: NotificationListContainer) =
- ShadeViewManager(
- context,
- listContainer,
- logger,
- viewBarn,
- notificationIconAreaController)
+ fun create(listContainer: NotificationListContainer, stackController: NotifStackController) =
+ ShadeViewManager(
+ context,
+ listContainer,
+ stackController,
+ logger,
+ viewBarn)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
index 2f496dd..a59d421 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -21,6 +21,7 @@
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.StatusBar
import com.android.wm.shell.bubbles.Bubbles
@@ -40,6 +41,7 @@
bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
+ stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
bindRowCallback: NotificationRowBinderImpl.BindRowCallback
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 9411de7..212c342e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.interruption.HeadsUpController
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
@@ -47,7 +48,7 @@
import dagger.Lazy
import java.io.FileDescriptor
import java.io.PrintWriter
-import java.util.*
+import java.util.Optional
import javax.inject.Inject
/**
@@ -85,6 +86,7 @@
bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
+ stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
bindRowCallback: NotificationRowBinderImpl.BindRowCallback
) {
@@ -112,7 +114,8 @@
newNotifPipeline.get().initialize(
notificationListener,
notificationRowBinder,
- listContainer)
+ listContainer,
+ stackController)
}
if (notifPipelineFlags.isNewPipelineEnabled()) {
@@ -152,8 +155,14 @@
}
override fun resetUserExpandedStates() {
- for (entry in entryManager.visibleNotifications) {
- entry.resetUserExpansion()
+ if (notifPipelineFlags.isNewPipelineEnabled()) {
+ for (entry in notifPipeline.get().allNotifs) {
+ entry.resetUserExpansion()
+ }
+ } else {
+ for (entry in entryManager.visibleNotifications) {
+ entry.resetUserExpansion()
+ }
}
}
@@ -167,9 +176,12 @@
}
}
- override fun getActiveNotificationsCount(): Int {
- return entryManager.activeNotificationsCount
- }
+ override fun getActiveNotificationsCount(): Int =
+ if (notifPipelineFlags.isNewPipelineEnabled()) {
+ notifPipeline.get().getShadeListCount()
+ } else {
+ entryManager.activeNotificationsCount
+ }
companion object {
// NOTE: The new pipeline is always active, even if the old pipeline is *rendering*.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
index eadf5ac..1c9af11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.StatusBar
import com.android.wm.shell.bubbles.Bubbles
@@ -42,6 +43,7 @@
bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
+ stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
bindRowCallback: NotificationRowBinderImpl.BindRowCallback
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9f10322..82ed34c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -54,9 +54,9 @@
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
-import android.util.Pair;
import android.util.Property;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -92,6 +92,8 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
+import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -131,7 +133,8 @@
* the group summary (which contains 1 or more child notifications).
*/
public class ExpandableNotificationRow extends ActivatableNotificationView
- implements PluginListener<NotificationMenuRowPlugin>, SwipeableView {
+ implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
+ NotificationFadeAware.FadeOptimizedNotification {
private static final boolean DEBUG = false;
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
@@ -144,6 +147,7 @@
private boolean mUpdateBackgroundOnUpdate;
private boolean mNotificationTranslationFinished = false;
private boolean mIsSnoozed;
+ private boolean mIsFaded;
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -812,6 +816,13 @@
mChildrenContainer.setUntruncatedChildCount(childCount);
}
+ /** Called after children have been attached to set the expansion states */
+ public void resetChildSystemExpandedStates() {
+ if (isSummaryWithChildren()) {
+ mChildrenContainer.updateExpansionStates();
+ }
+ }
+
/**
* Add a child notification to this view.
*
@@ -1673,12 +1684,13 @@
setTargetPoint(null);
}
- public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
+ /** Shows the given feedback icon, or hides the icon if null. */
+ public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
if (mIsSummaryWithChildren) {
- mChildrenContainer.showFeedbackIcon(show, resIds);
+ mChildrenContainer.setFeedbackIcon(icon);
}
- mPrivateLayout.showFeedbackIcon(show, resIds);
- mPublicLayout.showFeedbackIcon(show, resIds);
+ mPrivateLayout.setFeedbackIcon(icon);
+ mPublicLayout.setFeedbackIcon(icon);
}
/** Sets the last time the notification being displayed audibly alerted the user. */
@@ -2339,6 +2351,7 @@
onExpansionChanged(false /* userAction */, wasExpanded);
if (mIsSummaryWithChildren) {
mChildrenContainer.updateGroupOverflow();
+ resetChildSystemExpandedStates();
}
}
}
@@ -2774,17 +2787,112 @@
// alphas are reset
if (mChildrenContainer != null) {
mChildrenContainer.setAlpha(1.0f);
- mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
}
for (NotificationContentView l : mLayouts) {
l.setAlpha(1.0f);
- l.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ if (FADE_LAYER_OPTIMIZATION_ENABLED) {
+ setNotificationFaded(false);
+ } else {
+ setNotificationFadedOnChildren(false);
}
} else {
setHeadsUpAnimatingAway(false);
}
}
+ /** Gets the last value set with {@link #setNotificationFaded(boolean)} */
+ @Override
+ public boolean isNotificationFaded() {
+ return mIsFaded;
+ }
+
+ /**
+ * This class needs to delegate the faded state set on it by
+ * {@link com.android.systemui.statusbar.notification.stack.ViewState} to its children.
+ * Having each notification use layerType of HARDWARE anytime it fades in/out can result in
+ * extremely large layers (in the case of groups, it can even exceed the device height).
+ * Because these large renders can cause serious jank when rendering, we instead have
+ * notifications return false from {@link #hasOverlappingRendering()} and delegate the
+ * layerType to child views which really need it in order to render correctly, such as icon
+ * views or the conversation face pile.
+ *
+ * Another compounding factor for notifications is that we change clipping on each frame of the
+ * animation, so the hardware layer isn't able to do any caching at the top level, but the
+ * individual elements we render with hardware layers (e.g. icons) cache wonderfully because we
+ * never invalidate them.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ mIsFaded = faded;
+ if (childrenRequireOverlappingRendering()) {
+ // == Simple Scenario ==
+ // If a child (like remote input) needs this to have overlapping rendering, then set
+ // the layerType of this view and reset the children to render as if the notification is
+ // not fading.
+ NotificationFadeAware.setLayerTypeForFaded(this, faded);
+ setNotificationFadedOnChildren(false);
+ } else {
+ // == Delegating Scenario ==
+ // This is the new normal for alpha: Explicitly reset this view's layer type to NONE,
+ // and require that all children use their own hardware layer if they have bad
+ // overlapping rendering.
+ NotificationFadeAware.setLayerTypeForFaded(this, false);
+ setNotificationFadedOnChildren(faded);
+ }
+ }
+
+ /** Private helper for iterating over the layouts and children containers to set faded state */
+ private void setNotificationFadedOnChildren(boolean faded) {
+ delegateNotificationFaded(mChildrenContainer, faded);
+ for (NotificationContentView layout : mLayouts) {
+ delegateNotificationFaded(layout, faded);
+ }
+ }
+
+ private static void delegateNotificationFaded(@Nullable View view, boolean faded) {
+ if (FADE_LAYER_OPTIMIZATION_ENABLED && view instanceof NotificationFadeAware) {
+ ((NotificationFadeAware) view).setNotificationFaded(faded);
+ } else {
+ NotificationFadeAware.setLayerTypeForFaded(view, faded);
+ }
+ }
+
+ /**
+ * Only declare overlapping rendering if independent children of the view require it.
+ */
+ @Override
+ public boolean hasOverlappingRendering() {
+ return super.hasOverlappingRendering() && childrenRequireOverlappingRendering();
+ }
+
+ /**
+ * Because RemoteInputView is designed to be an opaque view that overlaps the Actions row, the
+ * row should require overlapping rendering to ensure that the overlapped view doesn't bleed
+ * through when alpha fading.
+ *
+ * Note that this currently works for top-level notifications which squish their height down
+ * while collapsing the shade, but does not work for children inside groups, because the
+ * accordion affect does not apply to those views, so super.hasOverlappingRendering() will
+ * always return false to avoid the clipping caused when the view's measured height is less than
+ * the 'actual height'.
+ */
+ private boolean childrenRequireOverlappingRendering() {
+ if (!FADE_LAYER_OPTIMIZATION_ENABLED) {
+ return true;
+ }
+ // The colorized background is another layer with which all other elements overlap
+ if (getEntry().getSbn().getNotification().isColorized()) {
+ return true;
+ }
+ // Check if the showing layout has a need for overlapping rendering.
+ // NOTE: We could check both public and private layouts here, but becuause these states
+ // don't animate well, there are bigger visual artifacts if we start changing the shown
+ // layout during shade expansion.
+ NotificationContentView showingLayout = getShowingLayout();
+ return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
+ }
+
@Override
public int getExtraBottomPadding() {
if (mIsSummaryWithChildren && isGroupExpanded()) {
@@ -3325,46 +3433,47 @@
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pwOriginal, String[] args) {
+ IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
// Skip super call; dump viewState ourselves
pw.println("Notification: " + mEntry.getKey());
- DumpUtilsKt.withIndenting(pw, ipw -> {
- ipw.print("visibility: " + getVisibility());
- ipw.print(", alpha: " + getAlpha());
- ipw.print(", translation: " + getTranslation());
- ipw.print(", removed: " + isRemoved());
- ipw.print(", expandAnimationRunning: " + mExpandAnimationRunning);
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
+ pw.print("visibility: " + getVisibility());
+ pw.print(", alpha: " + getAlpha());
+ pw.print(", translation: " + getTranslation());
+ pw.print(", removed: " + isRemoved());
+ pw.print(", expandAnimationRunning: " + mExpandAnimationRunning);
NotificationContentView showingLayout = getShowingLayout();
- ipw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
- ipw.println();
- showingLayout.dump(fd, ipw, args);
+ pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
+ pw.println();
+ showingLayout.dump(fd, pw, args);
if (getViewState() != null) {
- getViewState().dump(fd, ipw, args);
- ipw.println();
+ getViewState().dump(fd, pw, args);
+ pw.println();
} else {
- ipw.println("no viewState!!!");
+ pw.println("no viewState!!!");
}
if (mIsSummaryWithChildren) {
- ipw.println();
- ipw.print("ChildrenContainer");
- ipw.print(" visibility: " + mChildrenContainer.getVisibility());
- ipw.print(", alpha: " + mChildrenContainer.getAlpha());
- ipw.print(", translationY: " + mChildrenContainer.getTranslationY());
- ipw.println();
+ pw.println();
+ pw.print("ChildrenContainer");
+ pw.print(" visibility: " + mChildrenContainer.getVisibility());
+ pw.print(", alpha: " + mChildrenContainer.getAlpha());
+ pw.print(", translationY: " + mChildrenContainer.getTranslationY());
+ pw.println();
List<ExpandableNotificationRow> notificationChildren = getAttachedChildren();
- ipw.println("Children: " + notificationChildren.size());
- ipw.print("{");
- ipw.increaseIndent();
+ pw.println("Children: " + notificationChildren.size());
+ pw.print("{");
+ pw.increaseIndent();
for (ExpandableNotificationRow child : notificationChildren) {
- ipw.println();
- child.dump(fd, ipw, args);
+ pw.println();
+ child.dump(fd, pw, args);
}
- ipw.decreaseIndent();
- ipw.println("}");
+ pw.decreaseIndent();
+ pw.println("}");
} else if (mPrivateLayout != null) {
- mPrivateLayout.dumpSmartReplies(ipw);
+ mPrivateLayout.dumpSmartReplies(pw);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 0b29ae5..b28fb58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -20,10 +20,12 @@
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
@@ -32,10 +34,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.NodeController;
+import com.android.systemui.statusbar.notification.collection.render.NotifViewController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.dagger.AppName;
@@ -58,7 +62,8 @@
* Controller for {@link ExpandableNotificationRow}.
*/
@NotificationRowScope
-public class ExpandableNotificationRowController implements NodeController {
+public class ExpandableNotificationRowController implements NotifViewController {
+ private static final String TAG = "NotifRowController";
private final ExpandableNotificationRow mView;
private final NotificationListContainer mListContainer;
private final RemoteInputViewSubcomponent.Factory mRemoteInputViewSubcomponentFactory;
@@ -241,7 +246,7 @@
public void addChildAt(NodeController child, int index) {
ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
- mView.addChildNotification((ExpandableNotificationRow) child.getView());
+ mView.addChildNotification((ExpandableNotificationRow) child.getView(), index);
mListContainer.notifyGroupChildAdded(childView);
}
@@ -267,4 +272,28 @@
final List<ExpandableNotificationRow> mChildren = mView.getAttachedChildren();
return mChildren != null ? mChildren.size() : 0;
}
+
+ @Override
+ public void setUntruncatedChildCount(int childCount) {
+ if (mView.isSummaryWithChildren()) {
+ mView.setUntruncatedChildCount(childCount);
+ } else {
+ Log.w(TAG, "Called setUntruncatedChildCount(" + childCount + ") on a leaf row");
+ }
+ }
+
+ @Override
+ public void setSystemExpanded(boolean systemExpanded) {
+ mView.setSystemExpanded(systemExpanded);
+ }
+
+ @Override
+ public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
+ mView.setLastAudiblyAlertedMs(lastAudiblyAlertedMs);
+ }
+
+ @Override
+ public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
+ mView.setFeedbackIcon(icon);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index fa2c1ee..4b3d6f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -22,6 +22,7 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.IndentingPrintWriter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -743,15 +744,16 @@
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pwOriginal, String[] args) {
+ IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
pw.println(getClass().getSimpleName());
- DumpUtilsKt.withIndenting(pw, ipw -> {
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
ExpandableViewState viewState = getViewState();
if (viewState == null) {
- ipw.println("no viewState!!!");
+ pw.println("no viewState!!!");
} else {
- viewState.dump(fd, ipw, args);
- ipw.println();
+ viewState.dump(fd, pw, args);
+ pw.println();
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index b27a40a..e8e6e31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -20,6 +20,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.AttributeSet;
+import android.util.IndentingPrintWriter;
import android.view.View;
import com.android.systemui.R;
@@ -49,14 +50,15 @@
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pwOriginal, String[] args) {
+ IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
super.dump(fd, pw, args);
- DumpUtilsKt.withIndenting(pw, ipw -> {
- ipw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
- ipw.println("manageButton showHistory: " + mShowHistory);
- ipw.println("manageButton visibility: "
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
+ pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
+ pw.println("manageButton showHistory: " + mShowHistory);
+ pw.println("manageButton visibility: "
+ DumpUtilsKt.visibilityString(mDismissButton.getVisibility()));
- ipw.println("dismissButton visibility: "
+ pw.println("dismissButton visibility: "
+ DumpUtilsKt.visibilityString(mDismissButton.getVisibility()));
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index caba3ac..c661408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -28,6 +28,7 @@
import com.android.internal.widget.ConversationLayout;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
/**
* A hybrid view which may contain information about one ore more conversations.
@@ -138,4 +139,14 @@
lp.height = size;
view.setLayoutParams(lp);
}
+
+ /**
+ * Apply the faded state as a layer type change to the face pile view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mConversationFacePile, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index bc2adac3..c0d85a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -28,13 +28,14 @@
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.TransformState;
/**
* A hybrid view which may contain information about one ore more notifications.
*/
public class HybridNotificationView extends AlphaOptimizedLinearLayout
- implements TransformableView {
+ implements TransformableView, NotificationFadeAware {
protected final ViewTransformationHelper mTransformationHelper = new ViewTransformationHelper();
protected TextView mTitleView;
@@ -148,4 +149,7 @@
setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
mTransformationHelper.setVisible(visible);
}
+
+ @Override
+ public void setNotificationFaded(boolean faded) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 727f0e5..4dec1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -29,7 +29,6 @@
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -46,6 +45,8 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -73,7 +74,7 @@
* expanded and heads up layout. This class is responsible for clipping the content and and
* switching between the expanded, contracted and the heads up view depending on its clipped size.
*/
-public class NotificationContentView extends FrameLayout {
+public class NotificationContentView extends FrameLayout implements NotificationFadeAware {
private static final String TAG = "NotificationContentView";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -1655,15 +1656,16 @@
return null;
}
- public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
+ /** Shows the given feedback icon, or hides the icon if null. */
+ public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
if (mContractedChild != null) {
- mContractedWrapper.showFeedbackIcon(show, resIds);
+ mContractedWrapper.setFeedbackIcon(icon);
}
if (mExpandedChild != null) {
- mExpandedWrapper.showFeedbackIcon(show, resIds);
+ mExpandedWrapper.setFeedbackIcon(icon);
}
if (mHeadsUpChild != null) {
- mHeadsUpWrapper.showFeedbackIcon(show, resIds);
+ mHeadsUpWrapper.setFeedbackIcon(icon);
}
}
@@ -2045,6 +2047,41 @@
return Notification.COLOR_INVALID;
}
+ /**
+ * Delegate the faded state to the notification content views which actually
+ * need to have overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ if (mContractedWrapper != null) {
+ mContractedWrapper.setNotificationFaded(faded);
+ }
+ if (mHeadsUpWrapper != null) {
+ mHeadsUpWrapper.setNotificationFaded(faded);
+ }
+ if (mExpandedWrapper != null) {
+ mExpandedWrapper.setNotificationFaded(faded);
+ }
+ if (mSingleLineView != null) {
+ mSingleLineView.setNotificationFaded(faded);
+ }
+ }
+
+ /**
+ * @return true if a visible view has a remote input active, as this requires that the entire
+ * row report that it has overlapping rendering.
+ */
+ public boolean requireRowToHaveOverlappingRendering() {
+ // This inexpensive check is done on both states to avoid state invalidating the result.
+ if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
+ return true;
+ }
+ if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
+ return true;
+ }
+ return false;
+ }
+
public void setRemoteInputViewSubcomponentFactory(RemoteInputViewSubcomponent.Factory factory) {
mRemoteInputSubcomponentFactory = factory;
}
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 8e02d9f..6d13024 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
@@ -337,14 +337,15 @@
private void initializeFeedbackInfo(
final ExpandableNotificationRow row,
FeedbackInfo feedbackInfo) {
+ if (mAssistantFeedbackController.getFeedbackIcon(row.getEntry()) == null) {
+ return;
+ }
StatusBarNotification sbn = row.getEntry().getSbn();
UserHandle userHandle = sbn.getUser();
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
userHandle.getIdentifier());
- if (mAssistantFeedbackController.showFeedbackIndicator(row.getEntry())) {
- feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController);
- }
+ feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
index 12e94cb..bb43b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -21,6 +21,7 @@
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.CallLayout
import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -37,6 +38,7 @@
NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
private val callLayout: CallLayout = view as CallLayout
+ private lateinit var conversationIconContainer: View
private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandBtn: View
@@ -45,6 +47,8 @@
private fun resolveViews() {
with(callLayout) {
+ conversationIconContainer =
+ requireViewById(com.android.internal.R.id.conversation_icon_container)
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
@@ -82,4 +86,14 @@
}
override fun getMinLayoutHeight(): Int = minHeightWithActions
+
+ /**
+ * Apply the faded state as a layer type change to the face pile view which needs to have
+ * overlapping contents render precisely.
+ */
+ override fun setNotificationFaded(faded: Boolean) {
+ // Do not call super
+ NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
+ NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 3ef5b66..e136055 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -23,6 +23,7 @@
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingLinearLayout
import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform
@@ -42,6 +43,7 @@
)
private val conversationLayout: ConversationLayout = view as ConversationLayout
+ private lateinit var conversationIconContainer: View
private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandBtn: View
@@ -59,6 +61,8 @@
messagingLinearLayout = conversationLayout.messagingLinearLayout
imageMessageContainer = conversationLayout.imageMessageContainer
with(conversationLayout) {
+ conversationIconContainer =
+ requireViewById(com.android.internal.R.id.conversation_icon_container)
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
@@ -136,4 +140,10 @@
minHeightWithActions
else
super.getMinLayoutHeight()
+
+ override fun setNotificationFaded(faded: Boolean) {
+ // Do not call super
+ NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
+ NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index 4c9c2f9..fdff12d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -22,6 +22,7 @@
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -86,4 +87,14 @@
public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
return true;
}
+
+ /**
+ * Apply the faded state as a layer type change to the custom view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mView, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 8c6fa02..3159539 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -20,6 +20,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -67,4 +68,14 @@
}
super.onContentUpdated(row);
}
+
+ /**
+ * Apply the faded state as a layer type change to the custom view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mWrappedView, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 8ee9134..7a65436 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -21,7 +21,6 @@
import android.app.Notification;
import android.content.Context;
import android.util.ArraySet;
-import android.util.Pair;
import android.view.NotificationHeaderView;
import android.view.NotificationTopLineView;
import android.view.View;
@@ -32,12 +31,15 @@
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation;
+import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.ImageTransformState;
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -126,16 +128,17 @@
}
}
- /** Shows or hides feedback indicator */
+ /** Shows the given feedback icon, or hides the icon if null. */
@Override
- public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
+ public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
if (mFeedbackIcon != null) {
- mFeedbackIcon.setVisibility(show ? View.VISIBLE : View.GONE);
- if (show) {
+ mFeedbackIcon.setVisibility(icon != null ? View.VISIBLE : View.GONE);
+ if (icon != null) {
if (mFeedbackIcon instanceof ImageButton) {
- ((ImageButton) mFeedbackIcon).setImageResource(resIds.first);
+ ((ImageButton) mFeedbackIcon).setImageResource(icon.getIconRes());
}
- mFeedbackIcon.setContentDescription(mView.getContext().getString(resIds.second));
+ mFeedbackIcon.setContentDescription(
+ mView.getContext().getString(icon.getContentDescRes()));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 7630191..1c22f09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -29,7 +29,6 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
-import android.util.Pair;
import android.view.NotificationHeaderView;
import android.view.View;
import android.view.ViewGroup;
@@ -42,6 +41,8 @@
import com.android.settingslib.Utils;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -100,10 +101,8 @@
public void onContentUpdated(ExpandableNotificationRow row) {
}
- /**
- * Shows or hides feedback icon.
- */
- public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
+ /** Shows the given feedback icon, or hides the icon if null. */
+ public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
}
public void onReinflated() {
@@ -395,4 +394,13 @@
*/
public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
}
+
+ /**
+ * Apply the faded state as a layer type change to the views which need to have overlapping
+ * contents render precisely.
+ */
+ public void setNotificationFaded(boolean faded) {
+ NotificationFadeAware.setLayerTypeForFaded(getIcon(), faded);
+ NotificationFadeAware.setLayerTypeForFaded(getExpandButton(), faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index a472710..046a133 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -24,7 +24,6 @@
import android.graphics.drawable.ColorDrawable;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
-import android.util.Pair;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.NotificationHeaderView;
@@ -33,11 +32,15 @@
import android.widget.RemoteViews;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
+import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -51,7 +54,8 @@
/**
* A container containing child notifications
*/
-public class NotificationChildrenContainer extends ViewGroup {
+public class NotificationChildrenContainer extends ViewGroup
+ implements NotificationFadeAware {
@VisibleForTesting
static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
@@ -111,6 +115,7 @@
private int mCurrentHeaderTranslation = 0;
private float mHeaderVisibleAmount = 1.0f;
private int mUntruncatedChildCount;
+ private boolean mContainingNotificationIsFaded = false;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -277,6 +282,7 @@
mDividers.add(newIndex, divider);
row.setContentTransformationAmount(0, false /* isLastChild */);
+ row.setNotificationFaded(mContainingNotificationIsFaded);
// It doesn't make sense to keep old animations around, lets cancel them!
ExpandableViewState viewState = row.getViewState();
if (viewState != null) {
@@ -301,6 +307,7 @@
});
row.setSystemChildExpanded(false);
+ row.setNotificationFaded(false);
row.setUserLocked(false);
if (!row.isRemoved()) {
mGroupingUtil.restoreChildNotification(row);
@@ -473,7 +480,8 @@
return result;
}
- private void updateExpansionStates() {
+ /** To be called any time the rows have been updated */
+ public void updateExpansionStates() {
if (mChildrenExpanded || mUserLocked) {
// we don't modify it the group is expanded or if we are expanding it
return;
@@ -1288,15 +1296,13 @@
mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
}
- /**
- * Shows or hides feedback icon.
- */
- public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
+ /** Shows the given feedback icon, or hides the icon if null. */
+ public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
if (mNotificationHeaderWrapper != null) {
- mNotificationHeaderWrapper.showFeedbackIcon(show, resIds);
+ mNotificationHeaderWrapper.setFeedbackIcon(icon);
}
if (mNotificationHeaderWrapperLowPriority != null) {
- mNotificationHeaderWrapperLowPriority.showFeedbackIcon(show, resIds);
+ mNotificationHeaderWrapperLowPriority.setFeedbackIcon(icon);
}
}
@@ -1308,4 +1314,18 @@
mNotificationHeaderWrapperLowPriority.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
}
}
+
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ mContainingNotificationIsFaded = faded;
+ if (mNotificationHeaderWrapper != null) {
+ mNotificationHeaderWrapper.setNotificationFaded(faded);
+ }
+ if (mNotificationHeaderWrapperLowPriority != null) {
+ mNotificationHeaderWrapperLowPriority.setNotificationFaded(faded);
+ }
+ for (ExpandableNotificationRow child : mAttachedChildren) {
+ child.setNotificationFaded(faded);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index a97a54a..44e3718 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -47,6 +47,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
@@ -678,7 +679,7 @@
// TODO: move this logic to controller, which will invoke updateFooterView directly
boolean showDismissView = mClearAllEnabled &&
mController.hasActiveClearableNotifications(ROWS_ALL);
- boolean showFooterView = (showDismissView || getVisibleNotificationCount() > 0)
+ boolean showFooterView = (showDismissView || mController.getVisibleNotificationCount() > 0)
&& mIsCurrentUserSetup // see: b/193149550
&& mStatusBarState != StatusBarState.KEYGUARD
&& !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
@@ -1173,20 +1174,6 @@
}
}
- /**
- * Returns best effort count of visible notifications.
- */
- public int getVisibleNotificationCount() {
- int count = 0;
- for (int i = 0; i < getChildCount(); i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
- count++;
- }
- }
- return count;
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean isCurrentlyAnimating() {
return mStateAnimator.isRunning();
@@ -1458,7 +1445,7 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private float getAppearEndPosition() {
int appearPosition = 0;
- int visibleNotifCount = getVisibleNotificationCount();
+ int visibleNotifCount = mController.getVisibleNotificationCount();
if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
if (isHeadsUpTransition()
|| (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) {
@@ -4642,7 +4629,7 @@
}
private void ensureRemovedFromTransientContainer(View v) {
- if (v.getParent() == this && v instanceof SectionHeaderView) {
+ if (v.getParent() == this && v instanceof ExpandableView) {
ExpandableView expandableView = (ExpandableView) v;
ViewGroup transientContainer = expandableView.getTransientContainer();
// If the child is animating away, it will still have a parent, so
@@ -4915,7 +4902,8 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pwOriginal, String[] args) {
+ IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
StringBuilder sb = new StringBuilder("[")
.append(this.getClass().getSimpleName()).append(":")
.append(" pulsing=").append(mPulsing ? "T" : "f")
@@ -4929,15 +4917,15 @@
.append(" hideAmount=").append(mAmbientState.getHideAmount())
.append("]");
pw.println(sb.toString());
- DumpUtilsKt.withIndenting(pw, ipw -> {
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
int childCount = getChildCount();
- ipw.println("Number of children: " + childCount);
- ipw.println();
+ pw.println("Number of children: " + childCount);
+ pw.println();
for (int i = 0; i < childCount; i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
- child.dump(fd, ipw, args);
- ipw.println();
+ child.dump(fd, pw, args);
+ pw.println();
}
int transientViewCount = getTransientViewCount();
pw.println("Transient Views: " + transientViewCount);
@@ -6108,6 +6096,14 @@
return mExpandHelperCallback;
}
+ float getAppearFraction() {
+ return mLastSentAppear;
+ }
+
+ float getExpandedHeight() {
+ return mLastSentExpandedHeight;
+ }
+
/** Enum for selecting some or all notification rows (does not included non-notif views). */
@Retention(SOURCE)
@IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f7a3e3c..41a80c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -48,6 +48,7 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -98,6 +99,8 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
+import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
@@ -192,6 +195,8 @@
private final NotificationListContainerImpl mNotificationListContainer =
new NotificationListContainerImpl();
+ private final NotifStackController mNotifStackController =
+ new NotifStackControllerImpl();
@Nullable
private NotificationActivityStarter mNotificationActivityStarter;
@@ -294,6 +299,8 @@
}
};
+ private NotifStats mNotifStats = NotifStats.getEmpty();
+
private void updateResources() {
mNotificationDragDownMovement = mResources.getDimensionPixelSize(
R.dimen.lockscreen_shade_notification_movement);
@@ -866,6 +873,14 @@
mView.setHeadsUpAppearanceController(controller);
}
+ public float getAppearFraction() {
+ return mView.getAppearFraction();
+ }
+
+ public float getExpandedHeight() {
+ return mView.getExpandedHeight();
+ }
+
public void requestLayout() {
mView.requestLayout();
}
@@ -988,7 +1003,7 @@
}
public int getVisibleNotificationCount() {
- return mView.getVisibleNotificationCount();
+ return mNotifStats.getNumActiveNotifs();
}
public int getIntrinsicContentHeight() {
@@ -1183,7 +1198,7 @@
public void updateShowEmptyShadeView() {
mShowEmptyShadeView = mBarState != KEYGUARD
&& (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
- && mView.getVisibleNotificationCount() == 0;
+ && getVisibleNotificationCount() == 0;
mView.updateEmptyShadeView(
mShowEmptyShadeView,
@@ -1246,29 +1261,22 @@
}
public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
- if (mDynamicPrivacyController.isInLockedDownShade()) {
- return false;
+ boolean hasAlertingMatchingClearable = isClearable
+ ? mNotifStats.getHasClearableAlertingNotifs()
+ : mNotifStats.getHasNonClearableAlertingNotifs();
+ boolean hasSilentMatchingClearable = isClearable
+ ? mNotifStats.getHasClearableSilentNotifs()
+ : mNotifStats.getHasNonClearableSilentNotifs();
+ switch (selection) {
+ case ROWS_GENTLE:
+ return hasSilentMatchingClearable;
+ case ROWS_HIGH_PRIORITY:
+ return hasAlertingMatchingClearable;
+ case ROWS_ALL:
+ return hasSilentMatchingClearable || hasAlertingMatchingClearable;
+ default:
+ throw new IllegalStateException("Bad selection: " + selection);
}
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (!(child instanceof ExpandableNotificationRow)) {
- continue;
- }
- final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- final boolean matchClearable =
- isClearable ? row.canViewBeDismissed() : !row.canViewBeDismissed();
- final boolean inSection =
- NotificationStackScrollLayout.matchesSelection(row, selection);
- if (matchClearable && inSection) {
- if (mLegacyGroupManager == null
- || !mLegacyGroupManager.isSummaryOfSuppressedGroup(
- row.getEntry().getSbn())) {
- return true;
- }
- }
- }
- return false;
}
/**
@@ -1383,6 +1391,10 @@
return mNotificationListContainer;
}
+ public NotifStackController getNotifStackController() {
+ return mNotifStackController;
+ }
+
public void resetCheckSnoozeLeavebehind() {
mView.resetCheckSnoozeLeavebehind();
}
@@ -1394,17 +1406,6 @@
mVisibilityProvider.obtain(entry, true));
}
- /**
- * @return if the shade has currently any active notifications.
- */
- public boolean hasActiveNotifications() {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- return !mNotifPipeline.getShadeList().isEmpty();
- } else {
- return mNotificationEntryManager.hasActiveNotifications();
- }
- }
-
public void closeControlsIfOutsideTouch(MotionEvent ev) {
NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
@@ -1876,4 +1877,13 @@
}
}
}
+
+ private class NotifStackControllerImpl implements NotifStackController {
+ @Override
+ public void setNotifStats(@NonNull NotifStats notifStats) {
+ mNotifStats = notifStats;
+ updateFooter();
+ updateShowEmptyShadeView();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index eb7410c..1038e76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -468,8 +468,8 @@
final int height = (view instanceof ExpandableView)
? ((ExpandableView) view).getActualHeight()
: view.getHeight();
- final int rx = (int) ev.getX();
- final int ry = (int) ev.getY();
+ final int rx = (int) ev.getRawX();
+ final int ry = (int) ev.getRawY();
int[] temp = new int[2];
view.getLocationOnScreen(temp);
final int x = temp[0];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 6d82a45..83bea84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -29,6 +29,7 @@
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -206,14 +207,26 @@
} else if (view.getAlpha() != this.alpha) {
// apply layer type
boolean becomesFullyVisible = this.alpha == 1.0f;
- boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
- && view.hasOverlappingRendering();
- int layerType = view.getLayerType();
- int newLayerType = newLayerTypeIsHardware
- ? View.LAYER_TYPE_HARDWARE
- : View.LAYER_TYPE_NONE;
- if (layerType != newLayerType) {
- view.setLayerType(newLayerType, null);
+ boolean becomesFaded = !becomesInvisible && !becomesFullyVisible;
+ if (FadeOptimizedNotification.FADE_LAYER_OPTIMIZATION_ENABLED
+ && view instanceof FadeOptimizedNotification) {
+ // NOTE: A view that's going to utilize this interface to avoid having a hardware
+ // layer will have to return false from hasOverlappingRendering(), so we
+ // intentionally do not check that value in this if, even though we do in the else.
+ FadeOptimizedNotification fadeOptimizedView = (FadeOptimizedNotification) view;
+ boolean isFaded = fadeOptimizedView.isNotificationFaded();
+ if (isFaded != becomesFaded) {
+ fadeOptimizedView.setNotificationFaded(becomesFaded);
+ }
+ } else {
+ boolean newLayerTypeIsHardware = becomesFaded && view.hasOverlappingRendering();
+ int layerType = view.getLayerType();
+ int newLayerType = newLayerTypeIsHardware
+ ? View.LAYER_TYPE_HARDWARE
+ : View.LAYER_TYPE_NONE;
+ if (layerType != newLayerType) {
+ view.setLayerType(newLayerType, null);
+ }
}
// apply alpha
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 67f51cb..aa3b3e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.PowerManager;
@@ -46,9 +47,11 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
@@ -71,6 +74,7 @@
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
+ private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
@@ -167,6 +171,10 @@
private final MetricsLogger mMetricsLogger;
private final AuthController mAuthController;
+ private final StatusBarStateController mStatusBarStateController;
+
+ private long mLastFpFailureUptimeMillis;
+ private int mNumConsecutiveFpFailures;
private static final class PendingAuthenticated {
public final int userId;
@@ -209,7 +217,10 @@
BIOMETRIC_IRIS_FAILURE(403),
@UiEvent(doc = "A biometric event of type iris errored.")
- BIOMETRIC_IRIS_ERROR(404);
+ BIOMETRIC_IRIS_ERROR(404),
+
+ @UiEvent(doc = "Bouncer was shown as a result of consecutive failed UDFPS attempts.")
+ BIOMETRIC_BOUNCER_SHOWN(916);
private final int mId;
@@ -257,7 +268,8 @@
NotificationMediaManager notificationMediaManager,
WakefulnessLifecycle wakefulnessLifecycle,
ScreenLifecycle screenLifecycle,
- AuthController authController) {
+ AuthController authController,
+ StatusBarStateController statusBarStateController) {
mContext = context;
mPowerManager = powerManager;
mShadeController = shadeController;
@@ -279,6 +291,7 @@
mKeyguardBypassController.setUnlockController(this);
mMetricsLogger = metricsLogger;
mAuthController = authController;
+ mStatusBarStateController = statusBarStateController;
dumpManager.registerDumpable(getClass().getName(), this);
}
@@ -620,6 +633,22 @@
.setType(MetricsEvent.TYPE_FAILURE).setSubtype(toSubtype(biometricSourceType)));
Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
+
+ long currUptimeMillis = SystemClock.uptimeMillis();
+ if (currUptimeMillis - mLastFpFailureUptimeMillis < 2000) { // attempt within 2 seconds
+ mNumConsecutiveFpFailures += 1;
+ } else {
+ mNumConsecutiveFpFailures = 1;
+ }
+ mLastFpFailureUptimeMillis = currUptimeMillis;
+
+ if (biometricSourceType.equals(BiometricSourceType.FINGERPRINT)
+ && mUpdateMonitor.isUdfpsSupported()
+ && mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
+ mKeyguardViewController.showBouncer(true);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+ mNumConsecutiveFpFailures = 0;
+ }
cleanup();
}
@@ -631,6 +660,16 @@
.addTaggedData(MetricsEvent.FIELD_BIOMETRIC_AUTH_ERROR, msgId));
Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
+
+ // if we're on the shade and we're locked out, immediately show the bouncer
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT
+ && (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ && mUpdateMonitor.isUdfpsSupported()
+ && (mStatusBarStateController.getState() == StatusBarState.SHADE
+ || mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) {
+ mKeyguardViewController.showBouncer(true);
+ }
cleanup();
}
@@ -664,6 +703,8 @@
mBiometricModeListener.onResetMode();
mBiometricModeListener.notifyBiometricAuthModeChanged();
}
+ mNumConsecutiveFpFailures = 0;
+ mLastFpFailureUptimeMillis = 0;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 927b4c8..8a7cf36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -24,6 +24,7 @@
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -35,23 +36,29 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.ViewController;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* Controls the appearance of heads up notifications in the icon area and the header itself.
*/
-public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
- DarkIconDispatcher.DarkReceiver, NotificationWakeUpCoordinator.WakeUpListener {
+@StatusBarFragmentScope
+public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView>
+ implements OnHeadsUpChangedListener,
+ DarkIconDispatcher.DarkReceiver,
+ NotificationWakeUpCoordinator.WakeUpListener {
public static final int CONTENT_FADE_DURATION = 110;
public static final int CONTENT_FADE_DELAY = 100;
private final NotificationIconAreaController mNotificationIconAreaController;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationStackScrollLayoutController mStackScrollerController;
- private final HeadsUpStatusBarView mHeadsUpStatusBarView;
private final View mCenteredIconView;
private final View mClockView;
private final View mOperatorNameView;
@@ -67,8 +74,6 @@
@VisibleForTesting
float mExpandedHeight;
@VisibleForTesting
- boolean mIsExpanded;
- @VisibleForTesting
float mAppearFraction;
private ExpandableNotificationRow mTrackedChild;
private boolean mShown;
@@ -83,7 +88,7 @@
Point mPoint;
private KeyguardStateController mKeyguardStateController;
-
+ @Inject
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
HeadsUpManagerPhone headsUpManager,
@@ -92,11 +97,15 @@
KeyguardBypassController keyguardBypassController,
KeyguardStateController keyguardStateController,
NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue,
- NotificationPanelViewController notificationPanelViewController, View statusBarView) {
+ NotificationPanelViewController notificationPanelViewController,
+ @RootView PhoneStatusBarView statusBarView) {
this(notificationIconAreaController, headsUpManager, statusBarStateController,
keyguardBypassController, wakeUpCoordinator, keyguardStateController,
commandQueue, notificationStackScrollLayoutController,
notificationPanelViewController,
+ // TODO(b/205609837): We should have the StatusBarFragmentComponent provide these
+ // four views, and then we can delete this constructor and just use the one below
+ // (which also removes the undesirable @VisibleForTesting).
statusBarView.findViewById(R.id.heads_up_status_bar_view),
statusBarView.findViewById(R.id.clock),
statusBarView.findViewById(R.id.operator_name_frame),
@@ -118,25 +127,27 @@
View clockView,
View operatorNameView,
View centeredIconView) {
+ super(headsUpStatusBarView);
mNotificationIconAreaController = notificationIconAreaController;
mHeadsUpManager = headsUpManager;
- mHeadsUpManager.addListener(this);
- mHeadsUpStatusBarView = headsUpStatusBarView;
mCenteredIconView = centeredIconView;
- headsUpStatusBarView.setOnDrawingRectChangedListener(
- () -> updateIsolatedIconLocation(true /* requireUpdate */));
+
+ // We may be mid-HUN-expansion when this controller is re-created (for example, if the user
+ // has started pulling down the notification shade from the HUN and then the font size
+ // changes). We need to re-fetch these values since they're used to correctly display the
+ // HUN during this shade expansion.
+ mTrackedChild = notificationPanelViewController.getTrackedHeadsUpNotification();
+ mAppearFraction = stackScrollerController.getAppearFraction();
+ mExpandedHeight = stackScrollerController.getExpandedHeight();
+
mStackScrollerController = stackScrollerController;
mNotificationPanelViewController = notificationPanelViewController;
- notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
- notificationPanelViewController.setHeadsUpAppearanceController(this);
- mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScrollerController.setHeadsUpAppearanceController(this);
mClockView = clockView;
mOperatorNameView = operatorNameView;
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
- mDarkIconDispatcher.addDarkReceiver(this);
- mHeadsUpStatusBarView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
@@ -146,21 +157,32 @@
// trigger scroller to notify the latest panel translation
mStackScrollerController.requestLayout();
}
- mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
+ mView.removeOnLayoutChangeListener(this);
}
});
mBypassController = bypassController;
mStatusBarStateController = stateController;
mWakeUpCoordinator = wakeUpCoordinator;
- wakeUpCoordinator.addListener(this);
mCommandQueue = commandQueue;
mKeyguardStateController = keyguardStateController;
}
+ @Override
+ protected void onViewAttached() {
+ mHeadsUpManager.addListener(this);
+ mView.setOnDrawingRectChangedListener(
+ () -> updateIsolatedIconLocation(true /* requireUpdate */));
+ mWakeUpCoordinator.addListener(this);
+ mNotificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ mNotificationPanelViewController.setHeadsUpAppearanceController(this);
+ mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
+ mDarkIconDispatcher.addDarkReceiver(this);
+ }
- public void destroy() {
+ @Override
+ protected void onViewDetached() {
mHeadsUpManager.removeListener(this);
- mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
+ mView.setOnDrawingRectChangedListener(null);
mWakeUpCoordinator.removeListener(this);
mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
mNotificationPanelViewController.setHeadsUpAppearanceController(null);
@@ -170,7 +192,7 @@
private void updateIsolatedIconLocation(boolean requireStateUpdate) {
mNotificationIconAreaController.setIsolatedIconLocation(
- mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
+ mView.getIconDrawingRect(), requireStateUpdate);
}
@Override
@@ -184,20 +206,20 @@
if (shouldBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
}
- NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
- mHeadsUpStatusBarView.setEntry(newEntry);
+ NotificationEntry previousEntry = mView.getShowingEntry();
+ mView.setEntry(newEntry);
if (newEntry != previousEntry) {
boolean animateIsolation = false;
if (newEntry == null) {
// no heads up anymore, lets start the disappear animation
setShown(false);
- animateIsolation = !mIsExpanded;
+ animateIsolation = !isExpanded();
} else if (previousEntry == null) {
// We now have a headsUp and didn't have one before. Let's start the disappear
// animation
setShown(true);
- animateIsolation = !mIsExpanded;
+ animateIsolation = !isExpanded();
}
updateIsolatedIconLocation(false /* requireUpdate */);
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
@@ -210,8 +232,8 @@
mShown = isShown;
if (isShown) {
updateParentClipping(false /* shouldClip */);
- mHeadsUpStatusBarView.setVisibility(View.VISIBLE);
- show(mHeadsUpStatusBarView);
+ mView.setVisibility(View.VISIBLE);
+ show(mView);
hide(mClockView, View.INVISIBLE);
if (mCenteredIconView.getVisibility() != View.GONE) {
hide(mCenteredIconView, View.INVISIBLE);
@@ -227,21 +249,21 @@
if (mOperatorNameView != null) {
show(mOperatorNameView);
}
- hide(mHeadsUpStatusBarView, View.GONE, () -> {
+ hide(mView, View.GONE, () -> {
updateParentClipping(true /* shouldClip */);
});
}
// Show the status bar icons when the view gets shown / hidden
if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
mCommandQueue.recomputeDisableFlags(
- mHeadsUpStatusBarView.getContext().getDisplayId(), false);
+ mView.getContext().getDisplayId(), false);
}
}
}
private void updateParentClipping(boolean shouldClip) {
ViewClippingUtil.setClippingDeactivated(
- mHeadsUpStatusBarView, !shouldClip, mParentClippingParams);
+ mView, !shouldClip, mParentClippingParams);
}
/**
@@ -310,7 +332,7 @@
*/
public boolean shouldBeVisible() {
boolean notificationsShown = !mWakeUpCoordinator.getNotificationsFullyHidden();
- boolean canShow = !mIsExpanded && notificationsShown;
+ boolean canShow = !isExpanded() && notificationsShown;
if (mBypassController.getBypassEnabled() &&
(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
|| mKeyguardStateController.isKeyguardGoingAway())
@@ -328,17 +350,17 @@
public void setAppearFraction(float expandedHeight, float appearFraction) {
boolean changed = expandedHeight != mExpandedHeight;
+ boolean oldIsExpanded = isExpanded();
+
mExpandedHeight = expandedHeight;
mAppearFraction = appearFraction;
- boolean isExpanded = expandedHeight > 0;
// We only notify if the expandedHeight changed and not on the appearFraction, since
// otherwise we may run into an infinite loop where the panel and this are constantly
// updating themselves over just a small fraction
if (changed) {
updateHeadsUpHeaders();
}
- if (isExpanded != mIsExpanded) {
- mIsExpanded = isExpanded;
+ if (isExpanded() != oldIsExpanded) {
updateTopEntry();
}
}
@@ -358,6 +380,10 @@
}
}
+ private boolean isExpanded() {
+ return mExpandedHeight > 0;
+ }
+
private void updateHeadsUpHeaders() {
mHeadsUpManager.getAllEntries().forEach(entry -> {
updateHeader(entry);
@@ -376,22 +402,13 @@
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
- mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
+ mView.onDarkChanged(area, darkIntensity, tint);
}
public void onStateChanged() {
updateTopEntry();
}
- void readFrom(HeadsUpAppearanceController oldController) {
- if (oldController != null) {
- mTrackedChild = oldController.mTrackedChild;
- mExpandedHeight = oldController.mExpandedHeight;
- mIsExpanded = oldController.mIsExpanded;
- mAppearFraction = oldController.mAppearFraction;
- }
- }
-
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
updateTopEntry();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 407d287..88fe1ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -72,6 +72,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
@@ -154,6 +155,7 @@
private ControlsComponent mControlsComponent;
private boolean mControlServicesAvailable = false;
+ @Nullable private View mAmbientIndicationArea;
private ViewGroup mIndicationArea;
private TextView mIndicationText;
private TextView mIndicationTextBottom;
@@ -274,6 +276,29 @@
public void initFrom(KeyguardBottomAreaView oldBottomArea) {
setStatusBar(oldBottomArea.mStatusBar);
+
+ // if it exists, continue to use the original ambient indication container
+ // instead of the newly inflated one
+ if (mAmbientIndicationArea != null) {
+ // remove old ambient indication from its parent
+ View originalAmbientIndicationView =
+ oldBottomArea.findViewById(R.id.ambient_indication_container);
+ ((ViewGroup) originalAmbientIndicationView.getParent())
+ .removeView(originalAmbientIndicationView);
+
+ // remove current ambient indication from its parent (discard)
+ ViewGroup ambientIndicationParent = (ViewGroup) mAmbientIndicationArea.getParent();
+ int ambientIndicationIndex =
+ ambientIndicationParent.indexOfChild(mAmbientIndicationArea);
+ ambientIndicationParent.removeView(mAmbientIndicationArea);
+
+ // add the old ambient indication to this view
+ ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex);
+ mAmbientIndicationArea = originalAmbientIndicationView;
+
+ // update burn-in offsets
+ dozeTimeTick();
+ }
}
@Override
@@ -288,6 +313,7 @@
mQRCodeScannerButton = findViewById(R.id.qr_code_scanner_button);
mControlsButton = findViewById(R.id.controls_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
+ mAmbientIndicationArea = findViewById(R.id.ambient_indication_container);
mIndicationText = findViewById(R.id.keyguard_indication_text);
mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
mIndicationBottomMargin = getResources().getDimensionPixelSize(
@@ -923,6 +949,9 @@
int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */)
- mBurnInYOffset;
mIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
+ if (mAmbientIndicationArea != null) {
+ mAmbientIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
+ }
}
public void setAntiBurnInOffsetX(int burnInXOffset) {
@@ -931,6 +960,9 @@
}
mBurnInXOffset = burnInXOffset;
mIndicationArea.setTranslationX(burnInXOffset);
+ if (mAmbientIndicationArea != null) {
+ mAmbientIndicationArea.setTranslationX(burnInXOffset);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 353868b..9647486 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
@@ -81,6 +82,13 @@
public void onStrongAuthStateChanged(int userId) {
mBouncerPromptReason = mCallback.getBouncerPromptReason();
}
+
+ @Override
+ public void onLockedOutStateChanged(BiometricSourceType type) {
+ if (type == BiometricSourceType.FINGERPRINT) {
+ mBouncerPromptReason = mCallback.getBouncerPromptReason();
+ }
+ }
};
private final Runnable mRemoveViewRunnable = this::removeView;
private final KeyguardBypassController mKeyguardBypassController;
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 570b0ca..88ae0db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -37,7 +37,6 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.policy.BatteryController;
import java.io.FileDescriptor;
@@ -251,7 +250,7 @@
private void updateNavigation() {
if (mNavigationBarController != null
- && !QuickStepContract.isGesturalMode(mNavigationMode)) {
+ && mNavigationBarController.supportsIconTintForNavMode(mNavigationMode)) {
mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 9021b74..415fb92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -28,6 +28,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -230,6 +231,14 @@
}
/**
+ * Return whether to use the tint calculated in this class for nav icons.
+ */
+ public boolean supportsIconTintForNavMode(int navigationMode) {
+ // In gesture mode, we already do region sampling to update tint based on content beneath.
+ return !QuickStepContract.isGesturalMode(navigationMode);
+ }
+
+ /**
* Interface to apply a specific dark intensity.
*/
public interface DarkIntensityApplier {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 3f33281..68ab077 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -124,6 +124,9 @@
public void onAnimationEnd(Animator a) {
mLightsOutNotifView.setAlpha(showDot ? 1 : 0);
mLightsOutNotifView.setVisibility(showDot ? View.VISIBLE : View.GONE);
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ mLightsOutNotifView.animate().setListener(null);
}
})
.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a9753ac..db68ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -288,6 +288,7 @@
private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
private static final Rect EMPTY_RECT = new Rect();
+ private final InteractionJankMonitor mInteractionJankMonitor;
private final LayoutInflater mLayoutInflater;
private final FeatureFlags mFeatureFlags;
private final PowerManager mPowerManager;
@@ -480,9 +481,13 @@
private boolean mUserSetupComplete;
private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass;
- private ArrayList<Consumer<ExpandableNotificationRow>>
- mTrackingHeadsUpListeners =
- new ArrayList<>();
+ /**
+ * Non-null if there's a heads-up notification that we're currently tracking the position of.
+ */
+ @Nullable
+ private ExpandableNotificationRow mTrackedHeadsUpNotification;
+ private final ArrayList<Consumer<ExpandableNotificationRow>>
+ mTrackingHeadsUpListeners = new ArrayList<>();
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private int mPanelAlpha;
@@ -506,23 +511,11 @@
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
private final NotificationEntryManager mEntryManager;
- private final CommunalSourceMonitor.Callback mCommunalSourceMonitorCallback =
- new CommunalSourceMonitor.Callback() {
- @Override
- public void onSourceAvailable(WeakReference<CommunalSource> source) {
- setCommunalSource(source);
- }
- };
+ private final CommunalSourceMonitor.Callback mCommunalSourceMonitorCallback;
private WeakReference<CommunalSource> mCommunalSource;
- private final CommunalSource.Callback mCommunalSourceCallback =
- new CommunalSource.Callback() {
- @Override
- public void onDisconnected() {
- setCommunalSource(null /*source*/);
- }
- };
+ private final CommunalSource.Callback mCommunalSourceCallback;
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
@@ -781,7 +774,8 @@
PanelExpansionStateManager panelExpansionStateManager,
NotificationRemoteInputManager remoteInputManager,
Optional<SysUIUnfoldComponent> unfoldComponent,
- ControlsComponent controlsComponent) {
+ ControlsComponent controlsComponent,
+ InteractionJankMonitor interactionJankMonitor) {
super(view,
falsingManager,
dozeLog,
@@ -794,7 +788,8 @@
statusBarTouchableRegionManager,
lockscreenGestureLogger,
panelExpansionStateManager,
- ambientState);
+ ambientState,
+ interactionJankMonitor);
mView = view;
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
@@ -850,6 +845,7 @@
mTapAgainViewController = tapAgainViewController;
mUiExecutor = uiExecutor;
mSecureSettings = secureSettings;
+ mInteractionJankMonitor = interactionJankMonitor;
// TODO: inject via dagger instead of Dependency
mSysUiState = Dependency.get(SysUiState.class);
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
@@ -904,6 +900,15 @@
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
+
+ mCommunalSourceCallback = () -> {
+ mUiExecutor.execute(() -> setCommunalSource(null /*source*/));
+ };
+
+ mCommunalSourceMonitorCallback = (source) -> {
+ mUiExecutor.execute(() -> setCommunalSource(source));
+ };
+
updateUserSwitcherFlags();
onFinishInflate();
}
@@ -1895,14 +1900,16 @@
}
private void traceQsJank(boolean startTracing, boolean wasCancelled) {
- InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (mInteractionJankMonitor == null) {
+ return;
+ }
if (startTracing) {
- monitor.begin(mView, CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ mInteractionJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
} else {
if (wasCancelled) {
- monitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ mInteractionJankMonitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
} else {
- monitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ mInteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
}
}
}
@@ -3201,18 +3208,24 @@
mQsExpandImmediate = false;
mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false);
mTwoFingerQsExpandPossible = false;
- notifyListenersTrackingHeadsUp(null);
+ updateTrackingHeadsUp(null);
mExpandingFromHeadsUp = false;
setPanelScrimMinFraction(0.0f);
}
- private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+ private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) {
+ mTrackedHeadsUpNotification = pickedChild;
for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
listener.accept(pickedChild);
}
}
+ @Nullable
+ public ExpandableNotificationRow getTrackedHeadsUpNotification() {
+ return mTrackedHeadsUpNotification;
+ }
+
private void setListening(boolean listening) {
mKeyguardStatusBarViewController.setBatteryListening(listening);
if (mQs == null) return;
@@ -3449,7 +3462,7 @@
public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
if (pickedChild != null) {
- notifyListenersTrackingHeadsUp(pickedChild);
+ updateTrackingHeadsUp(pickedChild);
mExpandingFromHeadsUp = true;
}
// otherwise we update the state when the expansion is finished
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 2823d98..6a00591 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -184,6 +184,7 @@
protected final LockscreenGestureLogger mLockscreenGestureLogger;
private final PanelExpansionStateManager mPanelExpansionStateManager;
private final TouchHandler mTouchHandler;
+ private final InteractionJankMonitor mInteractionJankMonitor;
protected abstract void onExpandingFinished();
@@ -222,7 +223,8 @@
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
LockscreenGestureLogger lockscreenGestureLogger,
PanelExpansionStateManager panelExpansionStateManager,
- AmbientState ambientState) {
+ AmbientState ambientState,
+ InteractionJankMonitor interactionJankMonitor) {
mAmbientState = ambientState;
mView = view;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
@@ -273,6 +275,7 @@
mVibratorHelper = vibratorHelper;
mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
+ mInteractionJankMonitor = interactionJankMonitor;
}
protected void loadDimens() {
@@ -1411,17 +1414,26 @@
}
private void beginJankMonitoring(int cuj) {
+ if (mInteractionJankMonitor == null) {
+ return;
+ }
InteractionJankMonitor.Configuration.Builder builder =
InteractionJankMonitor.Configuration.Builder.withView(cuj, mView)
.setTag(isFullyCollapsed() ? "Expand" : "Collapse");
- InteractionJankMonitor.getInstance().begin(builder);
+ mInteractionJankMonitor.begin(builder);
}
private void endJankMonitoring(int cuj) {
+ if (mInteractionJankMonitor == null) {
+ return;
+ }
InteractionJankMonitor.getInstance().end(cuj);
}
private void cancelJankMonitoring(int cuj) {
+ if (mInteractionJankMonitor == null) {
+ return;
+ }
InteractionJankMonitor.getInstance().cancel(cuj);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 9cefded..bf54677 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -120,6 +120,9 @@
setAlpha(1f);
setTranslationX(0);
cancelLongClick();
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ animate().setListener(null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index fc549e2..1ad9fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -24,6 +24,7 @@
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
@@ -35,6 +36,7 @@
class SplitShadeHeaderController @Inject constructor(
@Named(SPLIT_SHADE_HEADER) private val statusBar: View,
private val statusBarIconController: StatusBarIconController,
+ private val privacyIconsController: HeaderPrivacyIconsController,
qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
featureFlags: FeatureFlags,
@Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController
@@ -64,8 +66,7 @@
return
}
field = value
- updateVisibility()
- updatePosition()
+ onShadeExpandedChanged()
}
var splitShadeMode = false
@@ -74,8 +75,7 @@
return
}
field = value
- updateVisibility()
- updateConstraints()
+ onSplitShadeModeChanged()
}
var shadeExpandedFraction = -1f
@@ -125,6 +125,26 @@
updateConstraints()
}
+ private fun onShadeExpandedChanged() {
+ if (shadeExpanded) {
+ privacyIconsController.startListening()
+ } else {
+ privacyIconsController.stopListening()
+ }
+ updateVisibility()
+ updatePosition()
+ }
+
+ private fun onSplitShadeModeChanged() {
+ if (splitShadeMode) {
+ privacyIconsController.onParentVisible()
+ } else {
+ privacyIconsController.onParentInvisible()
+ }
+ updateVisibility()
+ updateConstraints()
+ }
+
private fun updateVisibility() {
val visibility = if (!splitShadeMode && !combinedHeaders) {
View.GONE
@@ -167,4 +187,4 @@
statusBarIconController.removeIconGroup(iconManager)
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 94b010d..358d887 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -90,6 +90,7 @@
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
@@ -133,6 +134,7 @@
import com.android.systemui.InitController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.assist.AssistManager;
@@ -521,7 +523,6 @@
private QSPanelController mQSPanelController;
private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
- private final PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
KeyguardIndicationController mKeyguardIndicationController;
private View mReportRejectedTouch;
@@ -666,7 +667,6 @@
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
- private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
protected StatusBarNotificationPresenter mPresenter;
@@ -737,6 +737,7 @@
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
+ AccessibilityFloatingMenuController accessibilityFloatingMenuController,
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
@@ -768,7 +769,6 @@
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
- PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
@@ -808,7 +808,6 @@
mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManagerPhone;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
- mPhoneStatusBarViewControllerFactory = phoneStatusBarViewControllerFactory;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mDynamicPrivacyController = dynamicPrivacyController;
@@ -842,6 +841,7 @@
mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mNavigationBarController = navigationBarController;
+ mAccessibilityFloatingMenuController = accessibilityFloatingMenuController;
mAssistManagerLazy = assistManagerLazy;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -1057,6 +1057,8 @@
mBatteryController.observe(mLifecycle, mBatteryStateChangeCallback);
mLifecycle.setCurrentState(RESUMED);
+ mAccessibilityFloatingMenuController.init();
+
// set the initial view visibility
int disabledFlags1 = result.mDisabledFlags1;
int disabledFlags2 = result.mDisabledFlags2;
@@ -1154,12 +1156,8 @@
}
mStatusBarView = statusBarFragmentComponent.getPhoneStatusBarView();
-
- // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
- mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
- .create(mStatusBarView, mNotificationPanelViewController
- .getStatusBarTouchEventHandler());
- mPhoneStatusBarViewController.init();
+ mPhoneStatusBarViewController =
+ statusBarFragmentComponent.getPhoneStatusBarViewController();
// Ensure we re-propagate panel expansion values to the panel controller and
// any listeners it may have, such as PanelBar. This will also ensure we
@@ -1169,21 +1167,6 @@
mNotificationPanelViewController.updatePanelExpansionAndVisibility();
setBouncerShowingForStatusBarComponents(mBouncerShowing);
- HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
- if (mHeadsUpAppearanceController != null) {
- // This view is being recreated, let's destroy the old one
- mHeadsUpAppearanceController.destroy();
- }
- // TODO (b/136993073) Separate notification shade and status bar
- // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
- mHeadsUpAppearanceController = new HeadsUpAppearanceController(
- mNotificationIconAreaController, mHeadsUpManager,
- mStackScrollerController,
- mStatusBarStateController, mKeyguardBypassController,
- mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
- mNotificationPanelViewController, mStatusBarView);
- mHeadsUpAppearanceController.readFrom(oldController);
-
mLightsOutNotifController.setLightsOutNotifView(
mStatusBarView.findViewById(R.id.notification_lights_out));
mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
@@ -1488,6 +1471,7 @@
mBubblesOptional,
mPresenter,
mStackScrollerController.getNotificationListContainer(),
+ mStackScrollerController.getNotifStackController(),
mNotificationActivityStarter,
mPresenter);
}
@@ -1884,10 +1868,6 @@
return mDozeServiceHost.isPulsing();
}
- public boolean hideStatusBarIconsWhenExpanded() {
- return mNotificationPanelViewController.hideStatusBarIconsWhenExpanded();
- }
-
@Nullable
public View getAmbientIndicationContainer() {
return mAmbientIndicationContainer;
@@ -1909,10 +1889,6 @@
mScrimController.setKeyguardOccluded(occluded);
}
- public boolean headsUpShouldBeVisible() {
- return mHeadsUpAppearanceController.shouldBeVisible();
- }
-
/** A launch animation was cancelled. */
//TODO: These can / should probably be moved to NotificationPresenter or ShadeController
public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
@@ -1992,7 +1968,7 @@
}
}
- public void maybeEscalateHeadsUp() {
+ private void maybeEscalateHeadsUp() {
mHeadsUpManager.getAllEntries().forEach(entry -> {
final StatusBarNotification sbn = entry.getSbn();
final Notification notification = sbn.getNotification();
@@ -2003,6 +1979,7 @@
try {
EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
sbn.getKey());
+ wakeUpForFullScreenIntent();
notification.fullScreenIntent.send();
entry.notifyFullScreenIntentLaunched();
} catch (PendingIntent.CanceledException e) {
@@ -2012,6 +1989,17 @@
mHeadsUpManager.releaseAllImmediately();
}
+ void wakeUpForFullScreenIntent() {
+ if (isGoingToSleep() || mDozing) {
+ mPowerManager.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION,
+ "com.android.systemui:full_screen_intent");
+ mWakeUpComingFromTouch = false;
+ mWakeUpTouchLocation = null;
+ }
+ }
+
void makeExpandedVisible(boolean force) {
if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
@@ -2306,7 +2294,8 @@
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, PrintWriter pwOriginal, String[] args) {
+ IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
synchronized (mQueueLock) {
pw.println("Current Status Bar state:");
pw.println(" mExpandedVisible=" + mExpandedVisible);
@@ -2347,14 +2336,12 @@
}
pw.println(" mStackScroller: ");
if (mStackScroller != null) {
- DumpUtilsKt.withIndenting(pw, ipw -> {
- // Triple indent until we rewrite the rest of this dump()
- ipw.increaseIndent();
- ipw.increaseIndent();
- mStackScroller.dump(fd, ipw, args);
- ipw.decreaseIndent();
- ipw.decreaseIndent();
- });
+ // Double indent until we rewrite the rest of this dump()
+ pw.increaseIndent();
+ pw.increaseIndent();
+ mStackScroller.dump(fd, pw, args);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
}
pw.println(" Theme:");
String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + "";
@@ -3448,6 +3435,14 @@
return mNavigationBarController.getNavigationBarView(mDisplayId);
}
+ public void showPinningEnterExitToast(boolean entering) {
+ mNavigationBarController.showPinningEnterExitToast(mDisplayId, entering);
+ }
+
+ public void showPinningEscapeToast() {
+ mNavigationBarController.showPinningEscapeToast(mDisplayId);
+ }
+
/**
* TODO: Remove this method. Views should not be passed forward. Will cause theme issues.
* @return bottom area view
@@ -3541,7 +3536,7 @@
DejankUtils.startDetectingBlockingIpcs(tag);
updateRevealEffect(false /* wakingUp */);
updateNotificationPanelTouchState();
- notifyHeadsUpGoingToSleep();
+ maybeEscalateHeadsUp();
dismissVolumeDialog();
mWakeUpCoordinator.setFullyAwake(false);
mBypassHeadsUpNotifier.setFullyAwake(false);
@@ -3827,6 +3822,7 @@
private final DeviceProvisionedController mDeviceProvisionedController;
private final NavigationBarController mNavigationBarController;
+ private final AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
// UI-specific methods
@@ -4086,10 +4082,6 @@
}
}
- protected void notifyHeadsUpGoingToSleep() {
- maybeEscalateHeadsUp();
- }
-
/**
* @return Whether the security bouncer from Keyguard is showing.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index a77a097..05b4776 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -30,11 +30,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.media.AudioAttributes;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Log;
@@ -106,10 +106,8 @@
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@Inject
StatusBarCommandQueueCallbacks(
@@ -549,16 +547,12 @@
@Override
public void showPinningEnterExitToast(boolean entering) {
- if (mStatusBar.getNavigationBarView() != null) {
- mStatusBar.getNavigationBarView().showPinningEnterExitToast(entering);
- }
+ mStatusBar.showPinningEnterExitToast(entering);
}
@Override
public void showPinningEscapeToast() {
- if (mStatusBar.getNavigationBarView() != null) {
- mStatusBar.getNavigationBarView().showPinningEscapeToast();
- }
+ mStatusBar.showPinningEscapeToast();
}
@Override
@@ -611,9 +605,9 @@
}
private void vibrateForCameraGesture() {
- // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
mVibratorOptional.ifPresent(
- v -> v.vibrate(mCameraLaunchGestureVibrationEffect, VIBRATION_ATTRIBUTES));
+ v -> v.vibrate(mCameraLaunchGestureVibrationEffect,
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES));
}
private static VibrationEffect getCameraGestureVibrationEffect(
@@ -627,6 +621,8 @@
.compose();
}
if (vibratorOptional.isPresent() && vibratorOptional.get().hasAmplitudeControl()) {
+ // Make sure to pass -1 for repeat so VibratorManagerService doesn't stop us when going
+ // to sleep.
return VibrationEffect.createWaveform(
StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
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 7ab4a1e..e2bf0db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -441,6 +441,8 @@
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
public void showBouncer(boolean scrimmed) {
+ resetAlternateAuth(false);
+
if (mShowing && !mBouncer.isShowing()) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
}
@@ -553,12 +555,13 @@
public void onStartedWakingUp() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(false);
- View currentView = getCurrentNavBarView();
- if (currentView != null) {
- currentView.animate()
- .alpha(1f)
- .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
- .start();
+ NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ if (navBarView != null) {
+ navBarView.forEachView(view ->
+ view.animate()
+ .alpha(1f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start());
}
}
@@ -566,12 +569,13 @@
public void onStartedGoingToSleep() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(true);
- View currentView = getCurrentNavBarView();
- if (currentView != null) {
- currentView.animate()
- .alpha(0f)
- .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
- .start();
+ NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ if (navBarView != null) {
+ navBarView.forEachView(view ->
+ view.animate()
+ .alpha(0f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start());
}
}
@@ -1013,17 +1017,6 @@
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
- /**
- * Updates the visibility of the nav bar content views.
- */
- private void updateNavigationBarContentVisibility(boolean navBarContentVisible) {
- final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
- if (navBarView != null && navBarView.getCurrentView() != null) {
- final View currentView = navBarView.getCurrentView();
- currentView.setVisibility(navBarContentVisible ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
private View getCurrentNavBarView() {
final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
return navBarView != null ? navBarView.getCurrentView() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 11ed8cd..863ce57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -41,6 +41,8 @@
import android.util.EventLog;
import android.view.View;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
@@ -588,7 +590,8 @@
}
}
- private void handleFullScreenIntent(NotificationEntry entry) {
+ @VisibleForTesting
+ void handleFullScreenIntent(NotificationEntry entry) {
if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
if (shouldSuppressFullScreenIntent(entry)) {
mLogger.logFullScreenIntentSuppressedByDnD(entry.getKey());
@@ -612,6 +615,7 @@
try {
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
entry.getKey());
+ mStatusBar.wakeUpForFullScreenIntent();
fullscreenIntent.send();
entry.notifyFullScreenIntentLaunched();
mMetricsLogger.count("note_fullscreen", 1);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 9682c60..c8e1cdc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -192,6 +192,7 @@
initController.addPostInitTask(() -> {
mKeyguardIndicationController.init();
mViewHierarchyManager.setUpWithPresenter(this,
+ stackScrollerController.getNotifStackController(),
stackScrollerController.getNotificationListContainer());
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 1130ec2..43264b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -36,32 +36,25 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import androidx.annotation.Nullable;
+
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.animation.DialogListener;
-import com.android.systemui.animation.DialogListener.DismissReason;
-import com.android.systemui.animation.ListenableDialog;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-
/**
* Base class for dialogs that should appear over panels and keyguard.
* The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
* and dismisses itself when it receives the broadcast.
*/
-public class SystemUIDialog extends AlertDialog implements ListenableDialog,
- ViewRootImpl.ConfigChangedCallback {
+public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigChangedCallback {
// TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on.
private static final String FLAG_TABLET_DIALOG_WIDTH =
"persist.systemui.flag_tablet_dialog_width";
private final Context mContext;
private final DismissReceiver mDismissReceiver;
- private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>();
private final Handler mHandler = new Handler();
private int mLastWidth = Integer.MIN_VALUE;
@@ -115,10 +108,6 @@
mLastWidth = width;
mLastHeight = height;
getWindow().setLayout(width, height);
-
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onSizeChanged();
- }
}
@Override
@@ -195,60 +184,6 @@
ViewRootImpl.removeConfigCallback(this);
}
- @Override
- public void addListener(DialogListener listener) {
- mDialogListeners.add(listener);
- }
-
- @Override
- public void removeListener(DialogListener listener) {
- mDialogListeners.remove(listener);
- }
-
- @Override
- public void dismiss() {
- dismiss(DismissReason.UNKNOWN);
- }
-
- private void dismiss(DismissReason reason) {
- super.dismiss();
-
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onDismiss(reason);
- }
- }
-
- /**
- * Dismiss this dialog. If it was launched from another dialog using
- * {@link com.android.systemui.animation.DialogLaunchAnimator#showFromView} with a
- * non-{@code null} {@code parentHostDialog} parameter, also dismisses the stack of dialogs,
- * animating back to the original touchSurface.
- */
- public void dismissStack() {
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.prepareForStackDismiss();
- }
- dismiss();
- }
-
- @Override
- public void hide() {
- super.hide();
-
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onHide();
- }
- }
-
- @Override
- public void show() {
- super.show();
-
- for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onShow();
- }
- }
-
public void setShowForAllUsers(boolean show) {
setShowForAllUsers(this, show);
}
@@ -303,13 +238,32 @@
* the screen off / close system dialogs broadcast.
* <p>
* <strong>Note:</strong> Don't call dialog.setOnDismissListener() after
- * calling this because it causes a leak of BroadcastReceiver.
+ * calling this because it causes a leak of BroadcastReceiver. Instead, call the version that
+ * takes an extra Runnable as a parameter.
*
* @param dialog The dialog to be associated with the listener.
*/
public static void registerDismissListener(Dialog dialog) {
+ registerDismissListener(dialog, null);
+ }
+
+
+ /**
+ * Registers a listener that dismisses the given dialog when it receives
+ * the screen off / close system dialogs broadcast.
+ * <p>
+ * <strong>Note:</strong> Don't call dialog.setOnDismissListener() after
+ * calling this because it causes a leak of BroadcastReceiver.
+ *
+ * @param dialog The dialog to be associated with the listener.
+ * @param dismissAction An action to run when the dialog is dismissed.
+ */
+ public static void registerDismissListener(Dialog dialog, @Nullable Runnable dismissAction) {
DismissReceiver dismissReceiver = new DismissReceiver(dialog);
- dialog.setOnDismissListener(d -> dismissReceiver.unregister());
+ dialog.setOnDismissListener(d -> {
+ dismissReceiver.unregister();
+ if (dismissAction != null) dismissAction.run();
+ });
dismissReceiver.register();
}
@@ -343,11 +297,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- if (mDialog instanceof SystemUIDialog) {
- ((SystemUIDialog) mDialog).dismiss(DismissReason.DEVICE_LOCKED);
- } else {
- mDialog.dismiss();
- }
+ mDialog.dismiss();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
deleted file mode 100644
index 4f18f8c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.systemui.statusbar.phone
-
-import android.app.Dialog
-import android.content.Context
-import android.os.Bundle
-import android.view.ViewGroup
-import com.android.systemui.animation.HostDialogProvider
-
-/** An implementation of [HostDialogProvider] to be used when animating SysUI dialogs. */
-class SystemUIHostDialogProvider : HostDialogProvider {
- override fun createHostDialog(
- context: Context,
- theme: Int,
- onCreateCallback: () -> Unit,
- dismissOverride: (() -> Unit) -> Unit
- ): Dialog {
- return SystemUIHostDialog(context, theme, onCreateCallback, dismissOverride)
- }
-
- /**
- * This host dialog is a SystemUIDialog so that it's displayed above all SystemUI windows. Note
- * that it is not automatically dismissed when the device is locked, but only when the hosted
- * (original) dialog is dismissed. That way, the behavior of the dialog (dismissed when locking
- * or not) is consistent with when the dialog is shown with or without the dialog animator.
- */
- private class SystemUIHostDialog(
- context: Context,
- theme: Int,
- private val onCreateCallback: () -> Unit,
- private val dismissOverride: (() -> Unit) -> Unit
- ) : SystemUIDialog(context, theme, false /* dismissOnDeviceLock */) {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- onCreateCallback()
- }
-
- override fun dismiss() {
- dismissOverride {
- super.dismiss()
- }
- }
-
- override fun getWidth(): Int {
- return ViewGroup.LayoutParams.MATCH_PARENT
- }
-
- override fun getHeight(): Int {
- return ViewGroup.LayoutParams.MATCH_PARENT
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index b3f59b4..33171b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -28,6 +28,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
+import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -89,7 +90,6 @@
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -186,6 +186,7 @@
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
+ AccessibilityFloatingMenuController accessibilityFloatingMenuController,
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
@@ -217,7 +218,6 @@
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
- PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
@@ -289,6 +289,7 @@
visualStabilityManager,
deviceProvisionedController,
navigationBarController,
+ accessibilityFloatingMenuController,
assistManagerLazy,
configurationController,
notificationShadeWindowController,
@@ -319,7 +320,6 @@
extensionController,
userInfoControllerImpl,
operatorNameViewControllerFactory,
- phoneStatusBarViewControllerFactory,
phoneStatusBarPolicy,
keyguardIndicationController,
demoModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 8f11819..cb140adf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -33,6 +33,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -46,10 +47,10 @@
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.phone.TapAgainView;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
@@ -61,15 +62,12 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tuner.TunerService;
-import java.util.Optional;
-
import javax.inject.Named;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-@Module
+@Module(subcomponents = StatusBarFragmentComponent.class)
public abstract class StatusBarViewModule {
public static final String SPLIT_SHADE_HEADER = "split_shade_header";
@@ -174,6 +172,21 @@
/** */
@Provides
@StatusBarComponent.StatusBarScope
+ public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
+ @Named(SPLIT_SHADE_HEADER) View header) {
+ return header.findViewById(R.id.privacy_chip);
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ static StatusIconContainer providesStatusIconContainer(@Named(SPLIT_SHADE_HEADER) View header) {
+ return header.findViewById(R.id.statusIcons);
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
@Named(SPLIT_SHADE_BATTERY_VIEW)
static BatteryMeterView getBatteryMeterView(@Named(SPLIT_SHADE_HEADER) View view) {
return view.findViewById(R.id.batteryRemainingIcon);
@@ -243,7 +256,6 @@
NotificationPanelViewController notificationPanelViewController,
NetworkController networkController,
StatusBarStateController statusBarStateController,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
CommandQueue commandQueue,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory
@@ -261,7 +273,6 @@
notificationPanelViewController,
networkController,
statusBarStateController,
- statusBarOptionalLazy,
commandQueue,
collapsedStatusBarFragmentLogger,
operatorNameViewControllerFactory);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 2e3893a..d6ba6f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -55,7 +55,6 @@
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
@@ -71,12 +70,9 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -104,7 +100,6 @@
private View mCenteredIconArea;
private int mDisabled1;
private int mDisabled2;
- private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private DarkIconManager mDarkIconManager;
private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
private final CommandQueue mCommandQueue;
@@ -151,7 +146,6 @@
NotificationPanelViewController notificationPanelViewController,
NetworkController networkController,
StatusBarStateController statusBarStateController,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
CommandQueue commandQueue,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory
@@ -169,7 +163,6 @@
mNotificationPanelViewController = notificationPanelViewController;
mNetworkController = networkController;
mStatusBarStateController = statusBarStateController;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
mCommandQueue = commandQueue;
mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
@@ -329,8 +322,8 @@
}
protected int adjustDisableFlags(int state) {
- boolean headsUpVisible = mStatusBarOptionalLazy.get()
- .map(StatusBar::headsUpShouldBeVisible).orElse(false);
+ boolean headsUpVisible =
+ mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
if (headsUpVisible) {
state |= DISABLE_CLOCK;
}
@@ -399,10 +392,8 @@
}
private boolean shouldHideNotificationIcons() {
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
if (!mPanelExpansionStateManager.isClosed()
- && statusBarOptional.map(
- StatusBar::hideStatusBarIconsWhenExpanded).orElse(false)) {
+ && mNotificationPanelViewController.hideStatusBarIconsWhenExpanded()) {
return true;
}
return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 47c1875..3656ed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -18,7 +18,9 @@
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.BindsInstance;
@@ -56,6 +58,8 @@
// No one accesses this controller, so we need to make sure we reference it here so it does
// get initialized.
getBatteryMeterViewController().init();
+ getHeadsUpAppearanceController().init();
+ getPhoneStatusBarViewController().init();
}
/** */
@@ -66,4 +70,12 @@
@StatusBarFragmentScope
@RootView
PhoneStatusBarView getPhoneStatusBarView();
+
+ /** */
+ @StatusBarFragmentScope
+ PhoneStatusBarViewController getPhoneStatusBarViewController();
+
+ /** */
+ @StatusBarFragmentScope
+ HeadsUpAppearanceController getHeadsUpAppearanceController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 969361b..d244558 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -19,7 +19,9 @@
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.Module;
@@ -43,4 +45,16 @@
static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.battery);
}
+
+ /** */
+ @Provides
+ @StatusBarFragmentScope
+ static PhoneStatusBarViewController providePhoneStatusBarViewController(
+ PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
+ @RootView PhoneStatusBarView phoneStatusBarView,
+ NotificationPanelViewController notificationPanelViewController) {
+ return phoneStatusBarViewControllerFactory.create(
+ phoneStatusBarView,
+ notificationPanelViewController.getStatusBarTouchEventHandler());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
index 530da43..ef0a5b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
@@ -95,7 +95,6 @@
private val uiEventLogger: UiEventLogger
) : RemoteInputViewController {
- private object Token
private val onSendListeners = ArraySet<OnSendRemoteInputListener>()
private val resources get() = view.resources
@@ -179,8 +178,8 @@
entry.lastRemoteInputSent = SystemClock.elapsedRealtime()
entry.mRemoteEditImeAnimatingAway = true
- remoteInputController.addSpinning(entry.key, Token)
- remoteInputController.removeRemoteInput(entry, Token)
+ remoteInputController.addSpinning(entry.key, view.mToken)
+ remoteInputController.removeRemoteInput(entry, view.mToken)
remoteInputController.remoteInputSent(entry)
entry.setHasSentReply()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt
new file mode 100644
index 0000000..c6dbdb1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.statusbar.policy
+
+/**
+ * Interface for tracking packages with running foreground services and demoting foreground status
+ */
+interface RunningFgsController : CallbackController<RunningFgsController.Callback> {
+
+ /**
+ * @return A list of [UserPackageTime]s which have running foreground service(s)
+ */
+ fun getPackagesWithFgs(): List<UserPackageTime>
+
+ /**
+ * Stops all foreground services running as a package
+ * @param userId the userId the package is running under
+ * @param packageName the packageName
+ */
+ fun stopFgs(userId: Int, packageName: String)
+
+ /**
+ * Returns when the list of packages with foreground services changes
+ */
+ interface Callback {
+ /**
+ * The thing that
+ * @param packages the list of packages
+ */
+ fun onFgsPackagesChanged(packages: List<UserPackageTime>)
+ }
+
+ /**
+ * A triplet <user, packageName, timeMillis> where each element is a package running
+ * under a user that has had at least one foreground service running since timeMillis.
+ * Time should be derived from [SystemClock.elapsedRealtime].
+ */
+ data class UserPackageTime(val userId: Int, val packageName: String, val startTimeMillis: Long)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt
new file mode 100644
index 0000000..d44d365
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.statusbar.policy
+
+import android.app.IActivityManager
+import android.app.IForegroundServiceObserver
+import android.os.IBinder
+import android.os.RemoteException
+import android.util.Log
+import androidx.annotation.GuardedBy
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.policy.RunningFgsController.Callback
+import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime
+import com.android.systemui.util.time.SystemClock
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Implementation for [RunningFgsController]
+ */
+@SysUISingleton
+class RunningFgsControllerImpl @Inject constructor(
+ @Background private val executor: Executor,
+ private val systemClock: SystemClock,
+ private val activityManager: IActivityManager
+) : RunningFgsController, IForegroundServiceObserver.Stub() {
+
+ companion object {
+ private val LOG_TAG = RunningFgsControllerImpl::class.java.simpleName
+ }
+
+ private val lock = Any()
+
+ @GuardedBy("lock")
+ var initialized = false
+
+ @GuardedBy("lock")
+ private val runningServiceTokens = mutableMapOf<UserPackageKey, StartTimeAndTokensValue>()
+
+ @GuardedBy("lock")
+ private val callbacks = mutableSetOf<Callback>()
+
+ fun init() {
+ synchronized(lock) {
+ if (initialized) {
+ return
+ }
+ try {
+ activityManager.registerForegroundServiceObserver(this)
+ } catch (e: RemoteException) {
+ e.rethrowFromSystemServer()
+ }
+
+ initialized = true
+ }
+ }
+
+ override fun addCallback(listener: Callback) {
+ init()
+ synchronized(lock) { callbacks.add(listener) }
+ }
+
+ override fun removeCallback(listener: Callback) {
+ init()
+ synchronized(lock) {
+ if (!callbacks.remove(listener)) {
+ Log.e(LOG_TAG, "Callback was not registered.", RuntimeException())
+ }
+ }
+ }
+
+ override fun observe(lifecycle: Lifecycle?, listener: Callback?): Callback {
+ init()
+ return super.observe(lifecycle, listener)
+ }
+
+ override fun observe(owner: LifecycleOwner?, listener: Callback?): Callback {
+ init()
+ return super.observe(owner, listener)
+ }
+
+ override fun getPackagesWithFgs(): List<UserPackageTime> {
+ init()
+ return synchronized(lock) { getPackagesWithFgsLocked() }
+ }
+
+ private fun getPackagesWithFgsLocked(): List<UserPackageTime> =
+ runningServiceTokens.map {
+ UserPackageTime(it.key.userId, it.key.packageName, it.value.fgsStartTime)
+ }
+
+ override fun stopFgs(userId: Int, packageName: String) {
+ init()
+ try {
+ activityManager.makeServicesNonForeground(packageName, userId)
+ } catch (e: RemoteException) {
+ e.rethrowFromSystemServer()
+ }
+ }
+
+ private data class UserPackageKey(
+ val userId: Int,
+ val packageName: String
+ )
+
+ private class StartTimeAndTokensValue(systemClock: SystemClock) {
+ val fgsStartTime = systemClock.elapsedRealtime()
+ var tokens = mutableSetOf<IBinder>()
+ fun addToken(token: IBinder): Boolean {
+ return tokens.add(token)
+ }
+
+ fun removeToken(token: IBinder): Boolean {
+ return tokens.remove(token)
+ }
+
+ val isEmpty: Boolean
+ get() = tokens.isEmpty()
+ }
+
+ override fun onForegroundStateChanged(
+ token: IBinder,
+ packageName: String,
+ userId: Int,
+ isForeground: Boolean
+ ) {
+ val result = synchronized(lock) {
+ val userPackageKey = UserPackageKey(userId, packageName)
+ if (isForeground) {
+ var addedNew = false
+ runningServiceTokens.getOrPut(userPackageKey) {
+ addedNew = true
+ StartTimeAndTokensValue(systemClock)
+ }.addToken(token)
+ if (!addedNew) {
+ return
+ }
+ } else {
+ val startTimeAndTokensValue = runningServiceTokens[userPackageKey]
+ if (startTimeAndTokensValue?.removeToken(token) == false) {
+ Log.e(LOG_TAG,
+ "Stopped foreground service was not known to be running.")
+ return
+ }
+ if (!startTimeAndTokensValue!!.isEmpty) {
+ return
+ }
+ runningServiceTokens.remove(userPackageKey)
+ }
+ getPackagesWithFgsLocked().toList()
+ }
+
+ callbacks.forEach { executor.execute { it.onFgsPackagesChanged(result) } }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 85add6c..a537b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -219,6 +219,7 @@
private void clearLayoutLineCount(View view) {
if (view instanceof TextView) {
((TextView) view).nullLayouts();
+ view.forceLayout();
}
}
@@ -264,18 +265,29 @@
if (maxNumActions != -1 // -1 means 'no limit'
&& lp.mButtonType == SmartButtonType.ACTION
&& numShownActions >= maxNumActions) {
+ lp.mNoShowReason = "max-actions-shown";
// We've reached the maximum number of actions, don't add another one!
continue;
}
clearLayoutLineCount(child);
child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
+ if (((Button) child).getLayout() == null) {
+ Log.wtf(TAG, "Button layout is null after measure.");
+ }
coveredSuggestions.add(child);
final int lineCount = ((Button) child).getLineCount();
- if (lineCount < 1 || lineCount > 2) {
- // If smart reply has no text, or more than two lines, then don't show it.
+ if (lineCount < 1) {
+ // If smart reply has no text, then don't show it.
+ lp.mNoShowReason = "line-count-0";
+ continue;
+
+ }
+ if (lineCount > 2) {
+ // If smart reply has more than two lines, then don't show it.
+ lp.mNoShowReason = "line-count-3+";
continue;
}
@@ -324,6 +336,7 @@
markButtonsWithPendingSqueezeStatusAs(
LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions);
+ lp.mNoShowReason = "overflow";
// The current button doesn't fit, keep on adding lower-priority buttons in case
// any of those fit.
continue;
@@ -336,6 +349,7 @@
}
lp.show = true;
+ lp.mNoShowReason = "n/a";
displayedChildCount++;
if (lp.mButtonType == SmartButtonType.ACTION) {
numShownActions++;
@@ -349,6 +363,7 @@
for (View smartReplyButton : smartReplies) {
final LayoutParams lp = (LayoutParams) smartReplyButton.getLayoutParams();
lp.show = false;
+ lp.mNoShowReason = "not-enough-system-replies";
}
// Reset our measures back to when we had only added actions (before adding
// replies).
@@ -427,6 +442,8 @@
pw.print(lp.squeezeStatus);
pw.print(" show=");
pw.print(lp.show);
+ pw.print(" noShowReason=");
+ pw.print(lp.mNoShowReason);
pw.print(" view=");
pw.println(child);
}
@@ -498,6 +515,7 @@
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.show = false;
lp.squeezeStatus = LayoutParams.SQUEEZE_STATUS_NONE;
+ lp.mNoShowReason = "reset";
}
}
@@ -590,6 +608,9 @@
button.getPaddingLeft() + button.getPaddingRight() + textWidth
+ getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
button.measure(widthMeasureSpec, heightMeasureSpec);
+ if (button.getLayout() == null) {
+ Log.wtf(TAG, "Button layout is null after measure.");
+ }
final int newWidth = button.getMeasuredWidth();
@@ -772,6 +793,7 @@
private boolean show = false;
private int squeezeStatus = SQUEEZE_STATUS_NONE;
SmartButtonType mButtonType = SmartButtonType.REPLY;
+ String mNoShowReason = "new";
private LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 364c931..17dd26a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -69,6 +69,7 @@
import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
import com.android.systemui.SystemUISecondaryUserService;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -133,6 +134,7 @@
private final IActivityTaskManager mActivityTaskManager;
private final InteractionJankMonitor mInteractionJankMonitor;
private final LatencyTracker mLatencyTracker;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
@VisibleForTesting
@@ -180,7 +182,8 @@
@Background Executor bgExecutor,
InteractionJankMonitor interactionJankMonitor,
LatencyTracker latencyTracker,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mActivityManager = activityManager;
mUserTracker = userTracker;
@@ -208,6 +211,8 @@
mHandler = handler;
mActivityStarter = activityStarter;
mUserManager = userManager;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -351,7 +356,7 @@
boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
&& guestRecord == null;
boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
- && mUserManager.canAddMoreUsers();
+ && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
boolean createIsRestricted = !addUsersWhenLocked;
if (guestRecord == null) {
@@ -1179,7 +1184,7 @@
cancel();
} else {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
- dismissStack();
+ mDialogLaunchAnimator.dismissStack(this);
removeGuestUser(mGuestId, mTargetId);
}
}
@@ -1210,7 +1215,7 @@
if (which == BUTTON_NEGATIVE) {
cancel();
} else {
- dismissStack();
+ mDialogLaunchAnimator.dismissStack(this);
if (ActivityManager.isUserAMonkey()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt b/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt
index 9f33c27..f952476 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt
@@ -19,39 +19,15 @@
import android.util.IndentingPrintWriter
import android.view.View
import java.io.PrintWriter
-import java.util.function.Consumer
/**
- * Run some code that will print to an [IndentingPrintWriter] that wraps the given [PrintWriter].
+ * Get an [IndentingPrintWriter] which either is or wraps the given [PrintWriter].
*
- * If the given [PrintWriter] is an [IndentingPrintWriter], the block will be passed that same
- * instance with [IndentingPrintWriter.increaseIndent] having been called, and calling
- * [IndentingPrintWriter.decreaseIndent] after completion of the block, so the passed [PrintWriter]
- * should not be used before the block completes.
+ * The original [PrintWriter] should not be used until the returned [IndentingPrintWriter] is no
+ * longer being used, to avoid inconsistent writing.
*/
-inline fun PrintWriter.withIndenting(block: (IndentingPrintWriter) -> Unit) {
- if (this is IndentingPrintWriter) {
- this.withIncreasedIndent { block(this) }
- } else {
- block(IndentingPrintWriter(this))
- }
-}
-
-/**
- * Run some code that will print to an [IndentingPrintWriter] that wraps the given [PrintWriter].
- *
- * If the given [PrintWriter] is an [IndentingPrintWriter], the block will be passed that same
- * instance with [IndentingPrintWriter.increaseIndent] having been called, and calling
- * [IndentingPrintWriter.decreaseIndent] after completion of the block, so the passed [PrintWriter]
- * should not be used before the block completes.
- */
-fun PrintWriter.withIndenting(consumer: Consumer<IndentingPrintWriter>) {
- if (this is IndentingPrintWriter) {
- this.withIncreasedIndent { consumer.accept(this) }
- } else {
- consumer.accept(IndentingPrintWriter(this))
- }
-}
+fun PrintWriter.asIndenting(): IndentingPrintWriter =
+ (this as? IndentingPrintWriter) ?: IndentingPrintWriter(this)
/**
* Run some code inside a block, with [IndentingPrintWriter.increaseIndent] having been called on
@@ -66,6 +42,19 @@
}
}
+/**
+ * Run some code inside a block, with [IndentingPrintWriter.increaseIndent] having been called on
+ * the given argument, and calling [IndentingPrintWriter.decreaseIndent] after completion.
+ */
+fun IndentingPrintWriter.withIncreasedIndent(runnable: Runnable) {
+ this.increaseIndent()
+ try {
+ runnable.run()
+ } finally {
+ this.decreaseIndent()
+ }
+}
+
/** Return a readable string for the visibility */
fun visibilityString(@View.Visibility visibility: Int): String = when (visibility) {
View.GONE -> "gone"
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 63ca94c..6dd6d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -56,6 +56,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.ShellCommandHandler;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.nano.WmShellTraceProto;
@@ -114,6 +115,7 @@
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final Optional<ShellCommandHandler> mShellCommandHandler;
private final Optional<SizeCompatUI> mSizeCompatUIOptional;
+ private final Optional<DragAndDrop> mDragAndDropOptional;
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
@@ -142,6 +144,7 @@
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
Optional<ShellCommandHandler> shellCommandHandler,
Optional<SizeCompatUI> sizeCompatUIOptional,
+ Optional<DragAndDrop> dragAndDropOptional,
CommandQueue commandQueue,
ConfigurationController configurationController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -167,6 +170,7 @@
mProtoTracer = protoTracer;
mShellCommandHandler = shellCommandHandler;
mSizeCompatUIOptional = sizeCompatUIOptional;
+ mDragAndDropOptional = dragAndDropOptional;
mSysUiMainExecutor = sysUiMainExecutor;
}
@@ -182,6 +186,7 @@
mOneHandedOptional.ifPresent(this::initOneHanded);
mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
mSizeCompatUIOptional.ifPresent(this::initSizeCompatUi);
+ mDragAndDropOptional.ifPresent(this::initDragAndDrop);
}
@VisibleForTesting
@@ -396,6 +401,20 @@
mKeyguardUpdateMonitor.registerCallback(mSizeCompatUIKeyguardCallback);
}
+ void initDragAndDrop(DragAndDrop dragAndDrop) {
+ mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ dragAndDrop.onConfigChanged(newConfig);
+ }
+
+ @Override
+ public void onThemeChanged() {
+ dragAndDrop.onThemeChanged();
+ }
+ });
+ }
+
@Override
public void writeToProto(SystemUiTraceProto proto) {
if (proto.wmShell == null) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
index 6f2c565..ac1a83c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
@@ -80,8 +80,6 @@
// Explicitly disable one handed keyguard.
mTestableResources.addOverride(
R.bool.can_use_one_handed_bouncer, false);
- mTestableResources.addOverride(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning, false);
when(mKeyguardSecurityContainerControllerFactory.create(any(
KeyguardSecurityContainer.SecurityCallback.class)))
@@ -149,8 +147,6 @@
// Start disabled.
mTestableResources.addOverride(
R.bool.can_use_one_handed_bouncer, false);
- mTestableResources.addOverride(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning, false);
mKeyguardHostViewController.init();
assertEquals(
@@ -160,8 +156,6 @@
// And enable
mTestableResources.addOverride(
R.bool.can_use_one_handed_bouncer, true);
- mTestableResources.addOverride(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning, true);
mKeyguardHostViewController.updateResources();
assertEquals(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 64bdc2e..030464a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -18,8 +18,10 @@
import static android.view.WindowInsets.Type.ime;
+import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
+import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
+
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.eq;
@@ -27,7 +29,6 @@
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.when;
@@ -49,6 +50,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.GlobalSettings;
import org.junit.Before;
import org.junit.Rule;
@@ -107,6 +109,8 @@
private Resources mResources;
@Mock
private FalsingCollector mFalsingCollector;
+ @Mock
+ private GlobalSettings mGlobalSettings;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -140,7 +144,7 @@
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
- mConfigurationController, mFalsingCollector)
+ mConfigurationController, mFalsingCollector, mGlobalSettings)
.create(mSecurityCallback);
}
@@ -178,30 +182,13 @@
public void onResourcesUpdate_callsThroughOnRotationChange() {
// Rotation is the same, shouldn't cause an update
mKeyguardSecurityContainerController.updateResources();
- verify(mView, times(0)).setOneHandedMode(anyBoolean());
+ verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings);
// Update rotation. Should trigger update
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
mKeyguardSecurityContainerController.updateResources();
- verify(mView, times(1)).setOneHandedMode(anyBoolean());
- }
-
- @Test
- public void updateKeyguardPosition_callsThroughToViewInOneHandedMode() {
- when(mView.isOneHandedMode()).thenReturn(true);
- mKeyguardSecurityContainerController.updateKeyguardPosition(VIEW_WIDTH / 3f);
- verify(mView).setOneHandedModeLeftAligned(true, false);
-
- mKeyguardSecurityContainerController.updateKeyguardPosition((VIEW_WIDTH / 3f) * 2);
- verify(mView).setOneHandedModeLeftAligned(false, false);
- }
-
- @Test
- public void updateKeyguardPosition_ignoredInTwoHandedMode() {
- when(mView.isOneHandedMode()).thenReturn(false);
- mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f);
- verify(mView, never()).setOneHandedModeLeftAligned(anyBoolean(), anyBoolean());
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
}
private void touchDownLeftSide() {
@@ -228,7 +215,7 @@
@Test
public void onInterceptTap_inhibitsFalsingInOneHandedMode() {
- when(mView.isOneHandedMode()).thenReturn(true);
+ when(mView.getMode()).thenReturn(MODE_ONE_HANDED);
when(mView.isOneHandedModeLeftAligned()).thenReturn(true);
touchDownLeftSide();
@@ -251,83 +238,35 @@
}
@Test
- public void showSecurityScreen_oneHandedMode_bothFlagsDisabled_noOneHandedMode() {
- setUpKeyguardFlags(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */false);
-
+ public void showSecurityScreen_oneHandedMode_flagDisabled_noOneHandedMode() {
+ when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(false);
when(mKeyguardSecurityViewFlipperController.getSecurityView(
eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
- verify(mView).setOneHandedMode(false);
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
}
@Test
- public void showSecurityScreen_oneHandedMode_deviceFlagDisabled_noOneHandedMode() {
- setUpKeyguardFlags(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */true);
-
+ public void showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode() {
+ when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true);
when(mKeyguardSecurityViewFlipperController.getSecurityView(
eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
- verify(mView).setOneHandedMode(false);
+ verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings);
}
@Test
- public void showSecurityScreen_oneHandedMode_sysUiFlagDisabled_noOneHandedMode() {
- setUpKeyguardFlags(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */false);
-
- when(mKeyguardSecurityViewFlipperController.getSecurityView(
- eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
- .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
-
- mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
- verify(mView).setOneHandedMode(false);
- }
-
- @Test
- public void showSecurityScreen_oneHandedMode_bothFlagsEnabled_oneHandedMode() {
- setUpKeyguardFlags(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */true);
-
- when(mKeyguardSecurityViewFlipperController.getSecurityView(
- eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
- .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
-
- mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
- verify(mView).setOneHandedMode(true);
- }
-
- @Test
- public void showSecurityScreen_twoHandedMode_bothFlagsEnabled_noOneHandedMode() {
- setUpKeyguardFlags(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */true);
-
+ public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() {
+ when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true);
when(mKeyguardSecurityViewFlipperController.getSecurityView(
eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
.thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
- verify(mView).setOneHandedMode(false);
- }
-
- private void setUpKeyguardFlags(
- boolean deviceConfigCanUseOneHandedKeyguard,
- boolean sysuiResourceCanUseOneHandedKeyguard) {
- when(mResources.getBoolean(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning))
- .thenReturn(deviceConfigCanUseOneHandedKeyguard);
- when(mResources.getBoolean(
- R.bool.can_use_one_handed_bouncer))
- .thenReturn(sysuiResourceCanUseOneHandedKeyguard);
+ verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 2efd369..c751081 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -19,6 +19,9 @@
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
+import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -37,6 +40,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.settings.GlobalSettings;
import org.junit.Before;
import org.junit.Rule;
@@ -59,9 +63,10 @@
@Mock
private WindowInsetsController mWindowInsetsController;
-
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
+ @Mock
+ private GlobalSettings mGlobalSettings;
private KeyguardSecurityContainer mKeyguardSecurityContainer;
@@ -83,7 +88,7 @@
@Test
public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
- mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */true);
+ mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings);
int halfWidthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
@@ -94,7 +99,7 @@
@Test
public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
- mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -105,7 +110,7 @@
int imeInsetAmount = 100;
int systemBarInsetAmount = 10;
- mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -129,7 +134,7 @@
int imeInsetAmount = 0;
int systemBarInsetAmount = 10;
- mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -148,8 +153,8 @@
}
private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
- mKeyguardSecurityContainer.setOneHandedMode(oneHandedMode);
- mKeyguardSecurityContainer.setOneHandedModeLeftAligned(true, false);
+ int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
+ mKeyguardSecurityContainer.initMode(mode, mGlobalSettings);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH);
@@ -160,29 +165,28 @@
}
@Test
- public void setIsLeftAligned_movesKeyguard() {
+ public void updatePosition_movesKeyguard() {
setupForUpdateKeyguardPosition(/* oneHandedMode= */ true);
+ mKeyguardSecurityContainer.updatePositionByTouchX(
+ mKeyguardSecurityContainer.getWidth() - 1f);
- mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
- /* leftAligned= */false, /* animate= */false);
verify(mSecurityViewFlipper).setTranslationX(
mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
- mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
- /* leftAligned= */true, /* animate= */false);
+ mKeyguardSecurityContainer.updatePositionByTouchX(1f);
+
verify(mSecurityViewFlipper).setTranslationX(0.0f);
}
@Test
- public void setIsLeftAligned_doesntMoveTwoHandedKeyguard() {
+ public void updatePosition_doesntMoveTwoHandedKeyguard() {
setupForUpdateKeyguardPosition(/* oneHandedMode= */ false);
- mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
- /* leftAligned= */false, /* animate= */false);
+ mKeyguardSecurityContainer.updatePositionByTouchX(
+ mKeyguardSecurityContainer.getWidth() - 1f);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
- mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
- /* leftAligned= */true, /* animate= */false);
+ mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index de8cc89..ef9b850 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -1056,6 +1056,16 @@
verify(callback, atLeastOnce()).onRequireUnlockForNfc();
}
+ @Test
+ public void testFaceDoesNotAuth_afterPinAttempt() {
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ verify(mFingerprintManager, never()).authenticate(any(), any(), any(),
+ any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+ anyBoolean());
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt b/packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt
new file mode 100644
index 0000000..6fbe3ad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt
@@ -0,0 +1,41 @@
+/*
+ * 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
+
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+
+/**
+ * Fake [InstanceId] generator.
+ */
+class InstanceIdSequenceFake(instanceIdMax: Int) : InstanceIdSequence(instanceIdMax) {
+
+ /**
+ * Last id used to generate a [InstanceId]. `-1` if no [InstanceId] has been generated.
+ */
+ var lastInstanceId = -1
+ private set
+
+ override fun newInstanceId(): InstanceId {
+ if (lastInstanceId == -1 || lastInstanceId == mInstanceIdMax - 1) {
+ lastInstanceId = 1
+ } else {
+ lastInstanceId++
+ }
+ return newInstanceIdInternal(lastInstanceId)
+ }
+}
\ No newline at end of file
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 326d902..796af11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -100,11 +100,11 @@
@Test
public void enableWindowMagnification_passThrough() throws RemoteException {
mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
- Float.NaN, mAnimationCallback);
+ Float.NaN, 0f, 0f, mAnimationCallback);
waitForIdleSync();
verify(mWindowMagnificationController).enableWindowMagnification(eq(3.0f),
- eq(Float.NaN), eq(Float.NaN), eq(mAnimationCallback));
+ eq(Float.NaN), eq(Float.NaN), eq(0f), eq(0f), eq(mAnimationCallback));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
index 8bb9d42..44770fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
@@ -110,7 +110,7 @@
}
/**
- * Sets the given window insets to the current window metics.
+ * Sets the given window insets to the current window metrics.
*
* @param insets the window insets.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 854fc33..3cc177d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -17,22 +17,27 @@
package com.android.systemui.accessibility;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
@@ -40,6 +45,7 @@
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
@@ -56,7 +62,6 @@
import java.util.concurrent.atomic.AtomicReference;
-
@Ignore
@LargeTest
@RunWith(AndroidTestingRunner.class)
@@ -74,6 +79,8 @@
private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class);
private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class);
private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class);
+ private final ArgumentCaptor<Float> mOffsetXCaptor = ArgumentCaptor.forClass(Float.class);
+ private final ArgumentCaptor<Float> mOffsetYCaptor = ArgumentCaptor.forClass(Float.class);
@Mock
Handler mHandler;
@@ -94,10 +101,16 @@
private long mWaitingAnimationPeriod;
private long mWaitIntermediateAnimationPeriod;
+ private TestableWindowManager mWindowManager;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ mWindowManager = spy(new TestableWindowManager(wm));
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS;
mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
@@ -119,12 +132,15 @@
throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
- verify(mSpyController, atLeast(2)).enableWindowMagnification(
+ verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ mCenterXCaptor.capture(), mCenterYCaptor.capture(),
+ mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, 1.0f);
verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
+ verifyStartValue(mOffsetXCaptor, 0f);
+ verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
verify(mAnimationCallback).onResult(true);
}
@@ -162,8 +178,8 @@
});
SystemClock.sleep(mWaitingAnimationPeriod);
- verify(mSpyController).enableWindowMagnification(1, DEFAULT_CENTER_X,
- DEFAULT_CENTER_Y);
+ verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X,
+ DEFAULT_CENTER_Y, 0f, 0f);
verify(mAnimationCallback).onResult(true);
}
@@ -187,11 +203,15 @@
SystemClock.sleep(mWaitingAnimationPeriod);
- verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
+ mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture(),
+ mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, mCurrentScale.get());
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
+ verifyStartValue(mOffsetXCaptor, 0f);
+ verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
@@ -213,11 +233,15 @@
SystemClock.sleep(mWaitingAnimationPeriod);
- verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
+ mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture(),
+ mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, mCurrentScale.get());
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
+ verifyStartValue(mOffsetXCaptor, 0f);
+ verifyStartValue(mOffsetYCaptor, 0f);
// It presents the window magnification is disabled.
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
@@ -256,7 +280,7 @@
});
SystemClock.sleep(mWaitingAnimationPeriod);
- verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
+ verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
anyFloat());
verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
@@ -286,9 +310,10 @@
verify(mAnimationCallback).onResult(false);
SystemClock.sleep(mWaitingAnimationPeriod);
- verify(mSpyController, atLeast(2)).enableWindowMagnification(
+ verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ mCenterXCaptor.capture(), mCenterYCaptor.capture(),
+ mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
//Animating in reverse, so we only check if the start values are greater than current.
assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
@@ -336,7 +361,7 @@
});
SystemClock.sleep(mWaitingAnimationPeriod);
- verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
+ verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
anyFloat());
verify(mSpyController, never()).deleteWindowMagnification();
verify(mAnimationCallback).onResult(false);
@@ -362,23 +387,51 @@
SystemClock.sleep(mWaitingAnimationPeriod);
- verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
+ mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture(),
+ mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, mCurrentScale.get());
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
+ verifyStartValue(mOffsetXCaptor, 0f);
+ verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationCallback2).onResult(true);
}
@Test
+ public void enableWindowMagnificationWithOffset_expectedValues() {
+ final float offsetRatio = -0.1f;
+ final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+ mInstrumentation.runOnMainSync(() -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
+ windowBounds.exactCenterX(), windowBounds.exactCenterY(),
+ offsetRatio, offsetRatio, mAnimationCallback);
+ });
+ SystemClock.sleep(mWaitingAnimationPeriod);
+ final View attachedView = mWindowManager.getAttachedView();
+ assertNotNull(attachedView);
+ final Rect mirrorViewBound = new Rect();
+ final View mirrorView = attachedView.findViewById(R.id.surface_view);
+ assertNotNull(mirrorView);
+ mirrorView.getBoundsOnScreen(mirrorViewBound);
+
+ assertEquals(mirrorViewBound.exactCenterX() - windowBounds.exactCenterX(),
+ Math.round(offsetRatio * mirrorViewBound.width() / 2), 0.1f);
+ assertEquals(mirrorViewBound.exactCenterY() - windowBounds.exactCenterY(),
+ Math.round(offsetRatio * mirrorViewBound.height() / 2), 0.1f);
+ }
+
+ @Test
public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()
throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
- verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
+ verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
anyFloat());
verify(mAnimationCallback).onResult(true);
}
@@ -390,11 +443,15 @@
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
- verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
+ mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture(),
+ mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
+ verifyStartValue(mOffsetXCaptor, 0f);
+ verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(true);
}
@@ -433,8 +490,10 @@
mCurrentCenterY.set(mController.getCenterY());
});
SystemClock.sleep(mWaitingAnimationPeriod);
- verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
+ mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture(),
+ mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
//The animation is in verse, so we only check the start values should no be greater than
// the current one.
@@ -442,6 +501,8 @@
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
+ verifyStartValue(mOffsetXCaptor, 0f);
+ verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
@@ -471,9 +532,13 @@
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2);
- verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
- mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
+ mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture(),
+ mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
+ verifyStartValue(mOffsetXCaptor, 0f);
+ verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
@@ -571,9 +636,18 @@
}
@Override
- void enableWindowMagnification(float scale, float centerX, float centerY) {
- super.enableWindowMagnification(scale, centerX, centerY);
- mSpyController.enableWindowMagnification(scale, centerX, centerY);
+ void enableWindowMagnificationInternal(float scale, float centerX, float centerY) {
+ super.enableWindowMagnificationInternal(scale, centerX, centerY);
+ mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY);
+ }
+
+ @Override
+ void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
+ float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY) {
+ super.enableWindowMagnificationInternal(scale, centerX, centerY,
+ magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY);
+ mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY,
+ magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY);
}
@Override
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 9a30465..8fdcadd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -146,7 +146,7 @@
@Test
public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -159,7 +159,7 @@
@Test
public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
// Wait for Rects updated.
@@ -180,7 +180,7 @@
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
mInstrumentation.runOnMainSync(() -> {
- controller.enableWindowMagnification(Float.NaN, Float.NaN,
+ controller.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -195,7 +195,7 @@
@Test
public void deleteWindowMagnification_destroyControl() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -213,7 +213,7 @@
setSystemGestureInsets();
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
bounds.bottom);
});
ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
@@ -229,7 +229,7 @@
@Test
public void moveMagnifier_schedulesFrame() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
mWindowMagnificationController.moveWindowMagnifier(100f, 100f);
});
@@ -246,8 +246,8 @@
}).when(mHandler).postDelayed(any(Runnable.class), anyLong());
mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.enableWindowMagnification(2.0f, Float.NaN,
- Float.NaN));
+ () -> mWindowMagnificationController.enableWindowMagnificationInternal(2.0f,
+ Float.NaN, Float.NaN));
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
@@ -283,8 +283,8 @@
final float displayWidth = windowBounds.width();
final PointF magnifiedCenter = new PointF(center, center + 5f);
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, magnifiedCenter.x,
- magnifiedCenter.y);
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ magnifiedCenter.x, magnifiedCenter.y);
// Get the center again in case the center we set is out of screen.
magnifiedCenter.set(mWindowMagnificationController.getCenterX(),
mWindowMagnificationController.getCenterY());
@@ -327,7 +327,7 @@
testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
testWindowBounds.right + 100, testWindowBounds.bottom + 100);
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
mWindowManager.setWindowBounds(testWindowBounds);
@@ -347,7 +347,7 @@
@Test
public void screenSizeIsChangedToLarge_enabled_windowSizeIsConstrained() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
final int screenSize = mContext.getResources().getDimensionPixelSize(
@@ -369,7 +369,7 @@
@Test
public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
Mockito.reset(mWindowManager);
Mockito.reset(mMirrorWindowControl);
@@ -398,7 +398,7 @@
@Test
public void initializeA11yNode_enabled_expectedValues() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN,
Float.NaN);
});
final View mirrorView = mWindowManager.getAttachedView();
@@ -422,7 +422,7 @@
public void performA11yActions_visible_expectedResults() {
final int displayId = mContext.getDisplayId();
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN,
Float.NaN);
});
@@ -449,7 +449,7 @@
public void performA11yActions_visible_notifyAccessibilityActionPerformed() {
final int displayId = mContext.getDisplayId();
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN,
Float.NaN);
});
@@ -462,7 +462,7 @@
@Test
public void enableWindowMagnification_hasA11yWindowTitle() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -473,12 +473,12 @@
@Test
public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(0.9f, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(0.9f, Float.NaN,
Float.NaN);
});
@@ -489,7 +489,7 @@
public void onLocaleChanged_enabled_updateA11yWindowTitle() {
final String newA11yWindowTitle = "new a11y window title";
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
final TestableResources testableResources = getContext().getOrCreateTestableResources();
@@ -506,7 +506,7 @@
@Test
public void onSingleTap_enabled_scaleIsChanged() {
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -530,7 +530,7 @@
final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
setSystemGestureInsets();
mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
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 cdf40a1..8ca17b9 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
@@ -291,9 +291,12 @@
mTargetsObserver = spy(Dependency.get(AccessibilityButtonTargetsObserver.class));
mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ final AccessibilityFloatingMenuController controller =
+ new AccessibilityFloatingMenuController(mContextWrapper, mTargetsObserver,
+ mModeObserver, mKeyguardUpdateMonitor);
+ controller.init();
- return new AccessibilityFloatingMenuController(mContextWrapper, mTargetsObserver,
- mModeObserver, mKeyguardUpdateMonitor);
+ return controller;
}
private void enableAccessibilityFloatingMenuConfig() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 9bd33eb..f9ad740 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -2,35 +2,49 @@
import android.app.Dialog
import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
import android.os.Bundle
+import android.service.dreams.IDreamManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
import android.view.View
import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
+import com.android.internal.policy.DecorView
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogListener.DismissReason
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertTrue
import org.junit.After
+import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class DialogLaunchAnimatorTest : SysuiTestCase() {
private val launchAnimator = LaunchAnimator(context, isForTesting = true)
- private val hostDialogprovider = TestHostDialogProvider()
- private val dialogLaunchAnimator =
- DialogLaunchAnimator(context, launchAnimator, hostDialogprovider)
-
+ private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private val attachedViews = mutableSetOf<View>()
+ @Mock lateinit var dreamManager: IDreamManager
+ @get:Rule val rule = MockitoJUnit.rule()
+
+ @Before
+ fun setUp() {
+ dialogLaunchAnimator = DialogLaunchAnimator(context, launchAnimator, dreamManager)
+ }
+
@After
fun tearDown() {
runOnMainThreadAndWaitForIdleSync {
@@ -44,76 +58,66 @@
fun testShowDialogFromView() {
// Show the dialog. showFromView() must be called on the main thread with a dialog created
// on the main thread too.
- val (dialog, hostDialog) = createDialogAndHostDialog()
+ val dialog = createAndShowDialog()
- // Only the host dialog is actually showing.
- assertTrue(hostDialog.isShowing)
- assertFalse(dialog.isShowing)
+ assertTrue(dialog.isShowing)
- // The dialog onStart() method was called but not onStop().
- assertTrue(dialog.onStartCalled)
- assertFalse(dialog.onStopCalled)
+ // The dialog is now fullscreen.
+ val window = dialog.window
+ val decorView = window.decorView as DecorView
+ assertEquals(MATCH_PARENT, window.attributes.width)
+ assertEquals(MATCH_PARENT, window.attributes.height)
+ assertEquals(MATCH_PARENT, decorView.layoutParams.width)
+ assertEquals(MATCH_PARENT, decorView.layoutParams.height)
- // The dialog content has been stolen and is shown inside the host dialog.
- val hostDialogContent = hostDialog.findViewById<ViewGroup>(android.R.id.content)
- assertEquals(0, dialog.findViewById<ViewGroup>(android.R.id.content).childCount)
- assertEquals(1, hostDialogContent.childCount)
+ // The single DecorView child is a transparent fullscreen view that will dismiss the dialog
+ // when clicked.
+ assertEquals(1, decorView.childCount)
+ val transparentBackground = decorView.getChildAt(0) as ViewGroup
+ assertEquals(MATCH_PARENT, transparentBackground.layoutParams.width)
+ assertEquals(MATCH_PARENT, transparentBackground.layoutParams.height)
- // The original dialog content is added to another view that is the same size as the
- // original dialog window.
- val hostDialogRoot = hostDialogContent.getChildAt(0) as ViewGroup
- assertEquals(1, hostDialogRoot.childCount)
+ // The single transparent background child is a fake window with the same size and
+ // background as the dialog initially had.
+ assertEquals(1, transparentBackground.childCount)
+ val dialogContentWithBackground = transparentBackground.getChildAt(0) as ViewGroup
+ assertEquals(TestDialog.DIALOG_WIDTH, dialogContentWithBackground.layoutParams.width)
+ assertEquals(TestDialog.DIALOG_HEIGHT, dialogContentWithBackground.layoutParams.height)
+ assertEquals(dialog.windowBackground, dialogContentWithBackground.background)
- val dialogContentParent = hostDialogRoot.getChildAt(0) as ViewGroup
- assertEquals(1, dialogContentParent.childCount)
- assertEquals(TestDialog.DIALOG_WIDTH, dialogContentParent.layoutParams.width)
- assertEquals(TestDialog.DIALOG_HEIGHT, dialogContentParent.layoutParams.height)
+ // The dialog content is inside this fake window view.
+ assertNotNull(
+ dialogContentWithBackground.findViewByPredicate { it === dialog.contentView })
- val dialogContent = dialogContentParent.getChildAt(0)
- assertEquals(dialog.contentView, dialogContent)
- assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, dialogContent.layoutParams.width)
- assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, dialogContent.layoutParams.height)
-
- // Hiding/showing/dismissing the dialog should hide/show/dismiss the host dialog given that
- // it's a ListenableDialog.
- runOnMainThreadAndWaitForIdleSync { dialog.hide() }
- assertFalse(hostDialog.isShowing)
- assertFalse(dialog.isShowing)
-
- runOnMainThreadAndWaitForIdleSync { dialog.show() }
- assertTrue(hostDialog.isShowing)
- assertFalse(dialog.isShowing)
-
- assertFalse(dialog.onStopCalled)
+ // Clicking the transparent background should dismiss the dialog.
runOnMainThreadAndWaitForIdleSync {
// TODO(b/204561691): Remove this call to disableAllCurrentDialogsExitAnimations() and
// make sure that the test still pass on git_master/cf_x86_64_phone-userdebug in
// Forrest.
dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
- dialog.dismiss()
+ transparentBackground.performClick()
}
- assertFalse(hostDialog.isShowing)
assertFalse(dialog.isShowing)
- assertTrue(hostDialog.wasDismissed)
- assertTrue(dialog.onStopCalled)
}
@Test
fun testStackedDialogsDismissesAll() {
- val (_, hostDialogFirst) = createDialogAndHostDialog()
- val (dialogSecond, hostDialogSecond) = createDialogAndHostDialogFromDialog(hostDialogFirst)
+ val firstDialog = createAndShowDialog()
+ val secondDialog = createDialogAndShowFromDialog(firstDialog)
+ assertTrue(firstDialog.isShowing)
+ assertTrue(secondDialog.isShowing)
runOnMainThreadAndWaitForIdleSync {
dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
- dialogSecond.dismissStack()
+ dialogLaunchAnimator.dismissStack(secondDialog)
}
- assertTrue(hostDialogSecond.wasDismissed)
- assertTrue(hostDialogFirst.wasDismissed)
+ assertFalse(firstDialog.isShowing)
+ assertFalse(secondDialog.isShowing)
}
- private fun createDialogAndHostDialog(): Pair<TestDialog, TestHostDialog> {
+ private fun createAndShowDialog(): TestDialog {
return runOnMainThreadAndWaitForIdleSync {
val touchSurfaceRoot = LinearLayout(context)
val touchSurface = View(context)
@@ -125,22 +129,16 @@
attachedViews.add(touchSurfaceRoot)
val dialog = TestDialog(context)
- val hostDialog =
- dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog
- dialog to hostDialog
+ dialogLaunchAnimator.showFromView(dialog, touchSurface)
+ dialog
}
}
- private fun createDialogAndHostDialogFromDialog(
- hostParent: Dialog
- ): Pair<TestDialog, TestHostDialog> {
+ private fun createDialogAndShowFromDialog(animateFrom: Dialog): TestDialog {
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
- val hostDialog = dialogLaunchAnimator.showFromDialog(
- dialog,
- hostParent
- ) as TestHostDialog
- dialog to hostDialog
+ dialogLaunchAnimator.showFromDialog(dialog, animateFrom)
+ dialog
}
}
@@ -153,50 +151,14 @@
return result
}
- private class TestHostDialogProvider : HostDialogProvider {
- override fun createHostDialog(
- context: Context,
- theme: Int,
- onCreateCallback: () -> Unit,
- dismissOverride: (() -> Unit) -> Unit
- ): Dialog = TestHostDialog(context, onCreateCallback, dismissOverride)
- }
-
- private class TestHostDialog(
- context: Context,
- private val onCreateCallback: () -> Unit,
- private val dismissOverride: (() -> Unit) -> Unit
- ) : Dialog(context) {
- var wasDismissed = false
-
- init {
- // We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
- window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- onCreateCallback()
- }
-
- override fun dismiss() {
- dismissOverride {
- super.dismiss()
- wasDismissed = true
- }
- }
- }
-
- private class TestDialog(context: Context) : Dialog(context), ListenableDialog {
+ private class TestDialog(context: Context) : Dialog(context) {
companion object {
const val DIALOG_WIDTH = 100
const val DIALOG_HEIGHT = 200
}
- private val listeners = hashSetOf<DialogListener>()
val contentView = View(context)
- var onStartCalled = false
- var onStopCalled = false
+ val windowBackground = ColorDrawable(Color.RED)
init {
// We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
@@ -205,52 +167,10 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- window.setLayout(DIALOG_WIDTH, DIALOG_HEIGHT)
setContentView(contentView)
- }
- override fun onStart() {
- super.onStart()
- onStartCalled = true
- }
-
- override fun onStop() {
- super.onStart()
- onStopCalled = true
- }
-
- override fun addListener(listener: DialogListener) {
- listeners.add(listener)
- }
-
- override fun removeListener(listener: DialogListener) {
- listeners.remove(listener)
- }
-
- override fun dismiss() {
- super.dismiss()
- notifyListeners { onDismiss(DismissReason.UNKNOWN) }
- }
-
- override fun hide() {
- super.hide()
- notifyListeners { onHide() }
- }
-
- override fun show() {
- super.show()
- notifyListeners { onShow() }
- }
-
- fun dismissStack() {
- notifyListeners { prepareForStackDismiss() }
- dismiss()
- }
-
- private fun notifyListeners(notify: DialogListener.() -> Unit) {
- for (listener in HashSet(listeners)) {
- listener.notify()
- }
+ window.setLayout(DIALOG_WIDTH, DIALOG_HEIGHT)
+ window.setBackgroundDrawable(windowBackground)
}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
index 58e0cb2..3696ec5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
@@ -16,22 +16,53 @@
package com.android.systemui.animation
+import android.graphics.drawable.Drawable
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import android.widget.LinearLayout
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewParent
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class GhostedViewLaunchAnimatorControllerTest : SysuiTestCase() {
+ @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+ @Mock lateinit var view: View
+ @Mock lateinit var rootView: ViewGroup
+ @Mock lateinit var viewParent: ViewParent
+ @Mock lateinit var drawable: Drawable
+ lateinit var controller: GhostedViewLaunchAnimatorController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(view.rootView).thenReturn(rootView)
+ whenever(view.background).thenReturn(drawable)
+ whenever(view.height).thenReturn(0)
+ whenever(view.width).thenReturn(0)
+ whenever(view.parent).thenReturn(viewParent)
+ whenever(view.visibility).thenReturn(View.VISIBLE)
+ whenever(view.invalidate()).then { /* NO-OP */ }
+ whenever(view.getLocationOnScreen(any())).then { /* NO-OP */ }
+ whenever(interactionJankMonitor.begin(any(), anyInt())).thenReturn(true)
+ whenever(interactionJankMonitor.end(anyInt())).thenReturn(true)
+ controller = GhostedViewLaunchAnimatorController(view, 0, interactionJankMonitor)
+ }
+
@Test
fun animatingOrphanViewDoesNotCrash() {
- val ghostedView = LinearLayout(mContext)
- val controller = GhostedViewLaunchAnimatorController(ghostedView)
val state = LaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
controller.onIntentStarted(willAnimate = true)
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 0e86964..1cf21ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -351,9 +351,8 @@
mSystemClock.advanceTime(205);
mController.onTouchOutsideView();
- // THEN show the bouncer and reset alt auth
+ // THEN show the bouncer
verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
- verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
new file mode 100644
index 0000000..2ed38a4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.communal;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalSourcePrimerTest extends SysuiTestCase {
+ private static final String TEST_COMPONENT_NAME = "com.google.tests/.CommunalService";
+ private static final int MAX_RETRIES = 5;
+ private static final int RETRY_DELAY_MS = 1000;
+ private static final int CONNECTION_MIN_DURATION_MS = 5000;
+
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private Resources mResources;
+
+ private FakeSystemClock mFakeClock = new FakeSystemClock();
+ private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock);
+
+ @Mock
+ private CommunalSource mSource;
+
+ @Mock
+ private CommunalSourceMonitor mCommunalSourceMonitor;
+
+ @Mock
+ private CommunalSource.Connector mConnector;
+
+ @Mock
+ private CommunalSource.Observer mObserver;
+
+ private CommunalSourcePrimer mPrimer;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mResources.getInteger(R.integer.config_communalSourceMaxReconnectAttempts))
+ .thenReturn(MAX_RETRIES);
+ when(mResources.getInteger(R.integer.config_communalSourceReconnectBaseDelay))
+ .thenReturn(RETRY_DELAY_MS);
+ when(mResources.getInteger(R.integer.config_communalSourceReconnectBaseDelay))
+ .thenReturn(RETRY_DELAY_MS);
+ when(mResources.getString(R.string.config_communalSourceComponent))
+ .thenReturn(TEST_COMPONENT_NAME);
+ when(mResources.getInteger(R.integer.config_connectionMinDuration))
+ .thenReturn(CONNECTION_MIN_DURATION_MS);
+
+ mPrimer = new CommunalSourcePrimer(mContext, mResources, mFakeClock, mFakeExecutor,
+ mCommunalSourceMonitor, Optional.of(mConnector), Optional.of(mObserver));
+ }
+
+ @Test
+ public void testConnect() {
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.of(mSource));
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+ verify(mCommunalSourceMonitor).setSource(mSource);
+ }
+
+ @Test
+ public void testRetryOnBindFailure() throws Exception {
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.empty());
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+
+ // Verify attempts happen. Note that we account for the retries plus initial attempt, which
+ // is not scheduled.
+ for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
+ verify(mConnector, times(1)).connect();
+ clearInvocations(mConnector);
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ }
+
+ verify(mCommunalSourceMonitor, never()).setSource(Mockito.notNull());
+ }
+
+ @Test
+ public void testRetryOnDisconnectFailure() throws Exception {
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.of(mSource));
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+
+ // Verify attempts happen. Note that we account for the retries plus initial attempt, which
+ // is not scheduled.
+ for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
+ verify(mConnector, times(1)).connect();
+ clearInvocations(mConnector);
+ ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(CommunalSource.Callback.class);
+ verify(mSource).addCallback(callbackCaptor.capture());
+ clearInvocations(mSource);
+ verify(mCommunalSourceMonitor).setSource(Mockito.notNull());
+ clearInvocations(mCommunalSourceMonitor);
+ callbackCaptor.getValue().onDisconnected();
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ }
+
+ verify(mConnector, never()).connect();
+ }
+
+ @Test
+ public void testAttemptOnPackageChange() {
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.empty());
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+
+ final ArgumentCaptor<CommunalSource.Observer.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(CommunalSource.Observer.Callback.class);
+ verify(mObserver).addCallback(callbackCaptor.capture());
+
+ clearInvocations(mConnector);
+ callbackCaptor.getValue().onSourceChanged();
+
+ verify(mConnector, times(1)).connect();
+ }
+
+ @Test
+ public void testDisconnect() {
+ final ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(CommunalSource.Callback.class);
+
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.of(mSource));
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+ verify(mCommunalSourceMonitor).setSource(mSource);
+ verify(mSource).addCallback(callbackCaptor.capture());
+
+ clearInvocations(mConnector);
+ mFakeClock.advanceTime(CONNECTION_MIN_DURATION_MS + 1);
+ callbackCaptor.getValue().onDisconnected();
+ mFakeExecutor.runAllReady();
+
+ verify(mConnector).connect();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 3b1c5f3..bf5522c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -54,6 +54,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.model.SysUiState;
@@ -115,6 +116,7 @@
@Mock private UserContextProvider mUserContextProvider;
@Mock private StatusBar mStatusBar;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableLooper mTestableLooper;
@@ -159,8 +161,8 @@
mHandler,
mPackageManager,
Optional.of(mStatusBar),
- mKeyguardUpdateMonitor
- );
+ mKeyguardUpdateMonitor,
+ mDialogLaunchAnimator);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -218,7 +220,7 @@
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
- gestureListener.onSingleTapConfirmed(null);
+ gestureListener.onSingleTapUp(null);
verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
}
@@ -444,12 +446,12 @@
// When entering power menu from lockscreen, with smart lock enabled
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
- mGlobalActionsDialogLite.showOrHideDialog(true, true);
+ mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
// Then smart lock will be disabled
verify(mLockPatternUtils).requireCredentialEntry(eq(user));
// hide dialog again
- mGlobalActionsDialogLite.showOrHideDialog(true, true);
+ mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 6d8645e..b774daf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -41,6 +41,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardDisplayManager;
@@ -64,9 +65,6 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
-import java.util.Optional;
-import java.util.function.Function;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,6 +73,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+import java.util.function.Function;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -103,6 +104,7 @@
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private @Mock IKeyguardDrawnCallback mKeyguardDrawnCallback;
+ private @Mock InteractionJankMonitor mInteractionJankMonitor;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -121,6 +123,8 @@
.thenReturn(mUnfoldAnimationOptional);
when(mUnfoldAnimationOptional.isPresent()).thenReturn(true);
when(mUnfoldAnimationOptional.get()).thenReturn(mUnfoldAnimation);
+ when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
+ when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
mViewMediator = new KeyguardViewMediator(
mContext,
@@ -144,7 +148,8 @@
mKeyguardStateController,
() -> mKeyguardUnlockAnimationController,
mUnlockedScreenOffAnimationController,
- () -> mNotificationShadeDepthController);
+ () -> mNotificationShadeDepthController,
+ mInteractionJankMonitor);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index 175ec87f..a6e567e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -104,37 +104,54 @@
fun testPlayerOrdering() {
// Test values: key, data, last active time
val playingLocal = Triple("playing local",
- DATA.copy(active = true, isPlaying = true, isLocalSession = true, resumption = false),
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
4500L)
- val playingRemote = Triple("playing remote",
- DATA.copy(active = true, isPlaying = true, isLocalSession = false, resumption = false),
+ val playingCast = Triple("playing cast",
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL, resumption = false),
5000L)
val pausedLocal = Triple("paused local",
- DATA.copy(active = true, isPlaying = false, isLocalSession = true, resumption = false),
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
1000L)
- val pausedRemote = Triple("paused remote",
- DATA.copy(active = true, isPlaying = false, isLocalSession = false, resumption = false),
+ val pausedCast = Triple("paused cast",
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL, resumption = false),
2000L)
+ val playingRcn = Triple("playing RCN",
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_CAST_REMOTE, resumption = false),
+ 5000L)
+
+ val pausedRcn = Triple("paused RCN",
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_CAST_REMOTE, resumption = false),
+ 5000L)
+
val resume1 = Triple("resume 1",
- DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ DATA.copy(active = false, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
500L)
val resume2 = Triple("resume 2",
- DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ DATA.copy(active = false, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
1000L)
// Expected ordering for media players:
// Actively playing local sessions
- // Actively playing remote sessions
- // Paused sessions, by last active
+ // Actively playing cast sessions
+ // Paused local and cast sessions, by last active
+ // RCNs
// Resume controls, by last active
- val expected = listOf(playingLocal, playingRemote, pausedRemote, pausedLocal, resume2,
- resume1)
+ val expected = listOf(playingLocal, playingCast, pausedCast, pausedLocal, playingRcn,
+ pausedRcn, resume2, resume1)
expected.forEach {
clock.setCurrentTimeMillis(it.third)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 66b6470..f870da3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -74,8 +74,8 @@
mManager.addListener(mListener);
mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
- new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true,
- false, KEY, false, false, false, 0L);
+ new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null,
+ MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 2b2fc51..f44cc38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media
+import android.app.Notification
import android.app.Notification.MediaStyle
import android.app.PendingIntent
import android.app.smartspace.SmartspaceAction
@@ -229,6 +230,30 @@
}
@Test
+ fun testOnNotificationAdded_isRcn_markedRemote() {
+ val bundle = Bundle().apply {
+ putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Remote Cast Notification")
+ }
+ val rcn = SbnBuilder().run {
+ setPkg("com.android.systemui") // System package
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ it.addExtras(bundle)
+ }
+ build()
+ }
+
+ mediaDataManager.onNotificationAdded(KEY, rcn)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
+ assertThat(mediaDataCaptor.value!!.playbackLocation).isEqualTo(
+ MediaData.PLAYBACK_CAST_REMOTE)
+ }
+
+ @Test
fun testOnNotificationRemoved_callsListener() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
@@ -306,7 +331,8 @@
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false))
val data = mediaDataCaptor.value
- val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, isLocalSession = false)
+ val dataRemoteWithResume = data.copy(resumeAction = Runnable {},
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume)
// WHEN the notification is removed
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index 8dc9eff..421f9be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -42,7 +42,8 @@
val mockito = MockitoJUnit.rule()
companion object {
- val LOCAL = true
+ val LOCAL = MediaData.PLAYBACK_LOCAL
+ val REMOTE = MediaData.PLAYBACK_CAST_LOCAL
val RESUMPTION = true
val PLAYING = true
val UNDETERMINED = null
@@ -58,7 +59,7 @@
val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsRemote = mock(MediaControlPanel::class.java)
- val dataIsRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsRemote = createMediaData("app2", PLAYING, REMOTE, !RESUMPTION)
MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote, systemClock)
MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying, systemClock)
@@ -100,13 +101,13 @@
val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java)
- val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, REMOTE, !RESUMPTION)
val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java)
val dataIsStoppedAndLocal = createMediaData("app3", !PLAYING, LOCAL, !RESUMPTION)
val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java)
- val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, REMOTE, !RESUMPTION)
val playerCanResume = mock(MediaControlPanel::class.java)
val dataCanResume = createMediaData("app5", !PLAYING, LOCAL, RESUMPTION)
@@ -127,8 +128,8 @@
val players = MediaPlayerData.players()
assertThat(players).hasSize(6)
assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote,
- playerIsStoppedAndRemote, playerIsStoppedAndLocal, playerCanResume,
- playerUndetermined).inOrder()
+ playerIsStoppedAndRemote, playerIsStoppedAndLocal, playerUndetermined,
+ playerCanResume).inOrder()
}
@Test
@@ -160,9 +161,10 @@
private fun createMediaData(
app: String,
isPlaying: Boolean?,
- isLocalSession: Boolean,
+ location: Int,
resumption: Boolean
) =
- MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(), "",
- null, null, null, true, null, isLocalSession, resumption, null, false, isPlaying)
+ MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(),
+ "package:" + app, null, null, null, true, null, location, resumption, "key:" + app,
+ false, isPlaying)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index a17a03d..30ee2e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -211,10 +211,20 @@
}
@Test
- fun testOnLoad_remotePlayback_doesNotCheck() {
- // When media data is loaded that has not been checked yet, and is not local
- val dataRemote = data.copy(isLocalSession = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataRemote)
+ fun testOnLoad_localCast_doesNotCheck() {
+ // When media data is loaded that has not been checked yet, and is a local cast
+ val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
+ resumeListener.onMediaDataLoaded(KEY, null, dataCast)
+
+ // Then we do not take action
+ verify(mediaDataManager, never()).setResumeAction(any(), any())
+ }
+
+ @Test
+ fun testOnload_remoteCast_doesNotCheck() {
+ // When media data is loaded that has not been checked yet, and is a remote cast
+ val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE)
+ resumeListener.onMediaDataLoaded(KEY, null, dataRcn)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 52173c1..1b5e5eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -121,7 +121,6 @@
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
R.string.media_output_dialog_pairing_new));
}
@@ -139,7 +138,6 @@
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_SESSION_NAME);
}
@@ -156,7 +154,6 @@
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getString(
R.string.media_output_dialog_group));
}
@@ -165,14 +162,13 @@
public void onBindViewHolder_bindConnectedDevice_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
- assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
@Test
@@ -199,7 +195,6 @@
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
}
@@ -213,13 +208,10 @@
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
TEST_DEVICE_NAME_2);
- assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(
- mContext.getString(R.string.media_output_dialog_disconnected));
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -233,7 +225,6 @@
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText(
@@ -248,14 +239,13 @@
LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
- assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -268,7 +258,6 @@
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
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 053851e..4dac6d5 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
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.media.session.MediaSessionManager;
import android.os.Bundle;
import android.testing.AndroidTestingRunner;
@@ -70,6 +71,7 @@
private MediaOutputController mMediaOutputController;
private int mHeaderIconRes;
private IconCompat mIconCompat;
+ private Drawable mAppSourceDrawable;
private CharSequence mHeaderTitle;
private CharSequence mHeaderSubtitle;
@@ -173,6 +175,11 @@
}
@Override
+ Drawable getAppSourceIcon() {
+ return mAppSourceDrawable;
+ }
+
+ @Override
int getHeaderIconRes() {
return mHeaderIconRes;
}
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 09ec4ca..d71d98e 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
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -234,7 +235,7 @@
mMediaOutputController.onRequestFailed(0 /* reason */);
- verify(mCb).onRouteChanged();
+ verify(mCb, atLeastOnce()).onRouteChanged();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
index ca5d570..2c883a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
@@ -100,7 +100,6 @@
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getText(
@@ -114,7 +113,6 @@
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -140,7 +138,6 @@
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -165,7 +162,6 @@
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -183,7 +179,6 @@
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
new file mode 100644
index 0000000..efb4931
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.media.taptotransfer
+
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.mockito.any
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.StringWriter
+import java.util.concurrent.Executor
+
+@SmallTest
+class MediaTttChipControllerTest : SysuiTestCase() {
+
+ private lateinit var mediaTttChipController: MediaTttChipController
+
+ private val inlineExecutor = Executor { command -> command.run() }
+ private val commandRegistry = CommandRegistry(context, inlineExecutor)
+ private val pw = PrintWriter(StringWriter())
+
+ @Mock
+ private lateinit var windowManager: WindowManager
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mediaTttChipController = MediaTttChipController(context, commandRegistry, windowManager)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun constructor_addCommmandAlreadyRegistered() {
+ // Since creating the chip controller should automatically register the add command, it
+ // should throw when registering it again.
+ commandRegistry.registerCommand(
+ MediaTttChipController.ADD_CHIP_COMMAND_TAG
+ ) { EmptyCommand() }
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun constructor_removeCommmandAlreadyRegistered() {
+ // Since creating the chip controller should automatically register the remove command, it
+ // should throw when registering it again.
+ commandRegistry.registerCommand(
+ MediaTttChipController.REMOVE_CHIP_COMMAND_TAG
+ ) { EmptyCommand() }
+ }
+
+ @Test
+ fun addChipCommand_chipAdded() {
+ commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG))
+
+ verify(windowManager).addView(any(), any())
+ }
+
+ @Test
+ fun addChipCommand_twice_chipNotAddedTwice() {
+ commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG))
+ reset(windowManager)
+
+ commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG))
+ verify(windowManager, never()).addView(any(), any())
+ }
+
+ @Test
+ fun removeChipCommand_chipRemoved() {
+ // First, add the chip
+ commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG))
+
+ // Then, remove it
+ commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG))
+
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun removeChipCommand_noAdd_viewNotRemoved() {
+ commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG))
+
+ verify(windowManager, never()).removeView(any())
+ }
+
+ class EmptyCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ }
+
+ override fun help(pw: PrintWriter) {
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
new file mode 100644
index 0000000..a445d6f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.navigationbar;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+import dagger.Lazy;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NavBarHelperTest extends SysuiTestCase {
+
+ @Mock
+ AccessibilityManager mAccessibilityManager;
+ @Mock
+ AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ @Mock
+ AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ @Mock
+ OverviewProxyService mOverviewProxyService;
+ @Mock
+ Lazy<AssistManager> mAssistManagerLazy;
+ @Mock
+ AssistManager mAssistManager;
+ @Mock
+ NavigationModeController mNavigationModeController;
+ @Mock
+ UserTracker mUserTracker;
+ @Mock
+ ComponentName mAssistantComponent;
+ @Mock
+ DumpManager mDumpManager;
+ @Mock
+ NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater;
+
+ private NavBarHelper mNavBarHelper;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mAssistManagerLazy.get()).thenReturn(mAssistManager);
+ when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent);
+ when(mUserTracker.getUserId()).thenReturn(1);
+
+ mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
+ mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver,
+ mOverviewProxyService, mAssistManagerLazy, () -> Optional.of(mock(StatusBar.class)),
+ mNavigationModeController, mUserTracker, mDumpManager);
+
+ }
+
+ @Test
+ public void registerListenersInCtor() {
+ verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
+ verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
+ verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
+ }
+
+ @Test
+ public void registerAssistantContentObserver() {
+ mNavBarHelper.init();
+ verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
+ }
+
+ @Test
+ public void callbacksFiredWhenRegistering() {
+ mNavBarHelper.init();
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterConnecting() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onConnectionChanged(false);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+
+ mNavBarHelper.onConnectionChanged(true);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void a11yCallbacksFiredAfterModeChange() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterNavModeChange() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onNavigationModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void removeListenerNoCallbacksFired() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Remove listener
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Would have fired 2nd callback if not removed
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+}
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 4fc329f..9d2541c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.After;
@@ -82,11 +83,12 @@
mCommandQueue,
Dependency.get(Dependency.MAIN_HANDLER),
mock(ConfigurationController.class),
- mock(NavigationBarA11yHelper.class),
+ mock(NavBarHelper.class),
mock(TaskbarDelegate.class),
mNavigationBarFactory,
mock(DumpManager.class),
- mock(AutoHideController.class)));
+ mock(AutoHideController.class),
+ mock(LightBarController.class)));
initializeNavigationBars();
}
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 50b7171..5003013 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -76,6 +76,7 @@
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -90,6 +91,7 @@
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -103,6 +105,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.util.Optional;
@@ -135,8 +138,7 @@
EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
@Mock
EdgeBackGestureHandler mEdgeBackGestureHandler;
- @Mock
- NavigationBarA11yHelper mNavigationBarA11yHelper;
+ NavBarHelper mNavBarHelper;
@Mock
private LightBarController mLightBarController;
@Mock
@@ -179,6 +181,12 @@
mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
TestableLooper.get(this).runWithLooper(() -> {
+ mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
+ mock(AccessibilityManagerWrapper.class),
+ mock(AccessibilityButtonModeObserver.class), mOverviewProxyService,
+ () -> mock(AssistManager.class), () -> Optional.of(mStatusBar),
+ mock(NavigationModeController.class), mock(UserTracker.class),
+ mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
@@ -227,6 +235,7 @@
new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
.setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
.build());
+ when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
@@ -330,14 +339,14 @@
public void testA11yEventAfterDetach() {
View v = mNavigationBar.createView(null);
mNavigationBar.onViewAttachedToWindow(v);
- verify(mNavigationBarA11yHelper).registerA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).registerNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
mNavigationBar.onViewDetachedFromWindow(v);
- verify(mNavigationBarA11yHelper).removeA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).removeNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
// Should be safe even though the internal view is now null.
- mNavigationBar.updateAccessibilityServicesState();
+ mNavigationBar.updateAcessibilityStateFlags();
}
private NavigationBar createNavBar(Context context) {
@@ -367,7 +376,7 @@
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
- mNavigationBarA11yHelper,
+ mNavBarHelper,
mock(UserTracker.class),
mLightBarController,
mLightBarcontrollerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index d2bba36..26f04fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -1,6 +1,8 @@
package com.android.systemui.qs
+import android.os.Handler
import android.os.UserManager
+import android.provider.Settings
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -16,10 +18,12 @@
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.FooterActionsController.ExpansionState
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.leaks.FakeTunerService
import com.android.systemui.utils.leaks.LeakCheckedTest
import com.google.common.truth.Truth.assertThat
@@ -42,6 +46,8 @@
@Mock
private lateinit var userManager: UserManager
@Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var deviceProvisionedController: DeviceProvisionedController
@@ -62,11 +68,13 @@
private lateinit var view: FooterActionsView
private val falsingManager: FalsingManagerFake = FalsingManagerFake()
private lateinit var testableLooper: TestableLooper
+ private lateinit var fakeSettings: FakeSettings
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ fakeSettings = FakeSettings()
injectLeakCheckedDependencies(*LeakCheckedTest.ALL_SUPPORTED_CLASSES)
val fakeTunerService = Dependency.get(TunerService::class.java) as FakeTunerService
@@ -74,10 +82,11 @@
.inflate(R.layout.footer_actions, null) as FooterActionsView
controller = FooterActionsController(view, qsPanelController, activityStarter,
- userManager, userInfoController, multiUserSwitchController,
+ userManager, userTracker, userInfoController, multiUserSwitchController,
deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
globalActionsDialog, uiEventLogger, showPMLiteButton = true,
- buttonsVisibleState = ExpansionState.EXPANDED)
+ buttonsVisibleState = ExpansionState.EXPANDED, fakeSettings,
+ Handler(testableLooper.looper))
controller.init()
ViewUtils.attachView(view)
// View looper is the testable looper associated with the test
@@ -122,4 +131,24 @@
assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE)
}
+
+ @Test
+ fun testMultiUserSwitchUpdatedWhenSettingChanged() {
+ // When expanded, listening is true
+ controller.setListening(true)
+ testableLooper.processAllMessages()
+
+ val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
+ assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
+
+ // The setting is only used as an indicator for whether the view should refresh. The actual
+ // value of the setting is ignored; isMultiUserEnabled is the source of truth
+ whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true)
+
+ // Changing the value of USER_SWITCHER_ENABLED should cause the view to update
+ fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId)
+ testableLooper.processAllMessages()
+
+ assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
new file mode 100644
index 0000000..92743ae5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -0,0 +1,128 @@
+package com.android.systemui.qs
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyDialogController
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var privacyItemController: PrivacyItemController
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+ @Mock
+ private lateinit var privacyChip: OngoingPrivacyChip
+ @Mock
+ private lateinit var privacyDialogController: PrivacyDialogController
+ @Mock
+ private lateinit var privacyLogger: PrivacyLogger
+ @Mock
+ private lateinit var iconContainer: StatusIconContainer
+
+ private lateinit var cameraSlotName: String
+ private lateinit var microphoneSlotName: String
+ private lateinit var locationSlotName: String
+
+ private lateinit var controller: HeaderPrivacyIconsController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(privacyChip.context).thenReturn(context)
+ whenever(privacyChip.resources).thenReturn(context.resources)
+
+ cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
+ microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
+ locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
+
+ controller = HeaderPrivacyIconsController(
+ privacyItemController,
+ uiEventLogger,
+ privacyChip,
+ privacyDialogController,
+ privacyLogger,
+ iconContainer
+ )
+ }
+
+ @Test
+ fun testIgnoredSlotsOnParentVisible_noIndicators() {
+ setPrivacyController(micCamera = false, location = false)
+
+ controller.onParentVisible()
+
+ verify(iconContainer).removeIgnoredSlot(cameraSlotName)
+ verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).removeIgnoredSlot(locationSlotName)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnParentVisible_onlyMicCamera() {
+ setPrivacyController(micCamera = true, location = false)
+
+ controller.onParentVisible()
+
+ verify(iconContainer).addIgnoredSlot(cameraSlotName)
+ verify(iconContainer).addIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).removeIgnoredSlot(locationSlotName)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnParentVisible_onlyLocation() {
+ setPrivacyController(micCamera = false, location = true)
+
+ controller.onParentVisible()
+
+ verify(iconContainer).removeIgnoredSlot(cameraSlotName)
+ verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).addIgnoredSlot(locationSlotName)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnParentVisible_locationMicCamera() {
+ setPrivacyController(micCamera = true, location = true)
+
+ controller.onParentVisible()
+
+ verify(iconContainer).addIgnoredSlot(cameraSlotName)
+ verify(iconContainer).addIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).addIgnoredSlot(locationSlotName)
+ }
+
+ @Test
+ fun testPrivacyChipClicked() {
+ controller.onParentVisible()
+
+ val captor = argumentCaptor<View.OnClickListener>()
+ verify(privacyChip).setOnClickListener(capture(captor))
+
+ captor.value.onClick(privacyChip)
+
+ verify(privacyDialogController).showDialog(any(Context::class.java))
+ }
+
+ private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
+ whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
+ whenever(privacyItemController.locationAvailable).thenReturn(location)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
index f41d7b1..e2a0626 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
@@ -1,7 +1,6 @@
package com.android.systemui.qs
import android.testing.AndroidTestingRunner
-import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Before
@@ -9,7 +8,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -18,13 +16,9 @@
@SmallTest
class QSSquishinessControllerTest : SysuiTestCase() {
- @Mock private lateinit var qqsFooterActionsView: FooterActionsView
- @Mock private lateinit var qqsFooterActionsViewLP: ViewGroup.MarginLayoutParams
@Mock private lateinit var qsAnimator: QSAnimator
@Mock private lateinit var qsPanelController: QSPanelController
@Mock private lateinit var quickQsPanelController: QuickQSPanelController
- @Mock private lateinit var tileLayout: TileLayout
- @Mock private lateinit var pagedTileLayout: PagedTileLayout
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@@ -32,11 +26,8 @@
@Before
fun setup() {
- qsSquishinessController = QSSquishinessController(qqsFooterActionsView, qsAnimator,
+ qsSquishinessController = QSSquishinessController(qsAnimator,
qsPanelController, quickQsPanelController)
- `when`(quickQsPanelController.tileLayout).thenReturn(tileLayout)
- `when`(qsPanelController.tileLayout).thenReturn(pagedTileLayout)
- `when`(qqsFooterActionsView.layoutParams).thenReturn(qqsFooterActionsViewLP)
}
@Test
@@ -51,7 +42,7 @@
@Test
fun setSquishiness_updatesTiles() {
qsSquishinessController.squishiness = 0.5f
- verify(tileLayout).setSquishinessFraction(0.5f)
- verify(pagedTileLayout).setSquishinessFraction(0.5f)
+ verify(qsPanelController).setSquishinessFraction(0.5f)
+ verify(quickQsPanelController).setSquishinessFraction(0.5f)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 815c818..07c8af9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -20,7 +20,6 @@
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterViewController
@@ -28,11 +27,6 @@
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.privacy.OngoingPrivacyChip
-import com.android.systemui.privacy.PrivacyDialogController
-import com.android.systemui.privacy.PrivacyItemController
-import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -52,10 +46,10 @@
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -65,11 +59,7 @@
@Mock
private lateinit var view: QuickStatusBarHeader
@Mock
- private lateinit var privacyItemController: PrivacyItemController
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var uiEventLogger: UiEventLogger
+ private lateinit var privacyIconsController: HeaderPrivacyIconsController
@Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
@@ -81,18 +71,12 @@
@Mock
private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock
- private lateinit var privacyLogger: PrivacyLogger
- @Mock
private lateinit var colorExtractor: SysuiColorExtractor
@Mock
private lateinit var iconContainer: StatusIconContainer
@Mock
private lateinit var qsCarrierGroup: QSCarrierGroup
@Mock
- private lateinit var privacyChip: OngoingPrivacyChip
- @Mock
- private lateinit var privacyDialogController: PrivacyDialogController
- @Mock
private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
@Mock
private lateinit var variableDateViewController: VariableDateViewController
@@ -115,10 +99,6 @@
private lateinit var controller: QuickStatusBarHeaderController
- private lateinit var cameraSlotName: String
- private lateinit var microphoneSlotName: String
- private lateinit var locationSlotName: String
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -131,25 +111,14 @@
`when`(view.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context)
- cameraSlotName = mContext.resources.getString(
- com.android.internal.R.string.status_bar_camera)
- microphoneSlotName = mContext.resources.getString(
- com.android.internal.R.string.status_bar_microphone)
- locationSlotName = mContext.resources.getString(
- com.android.internal.R.string.status_bar_location)
-
controller = QuickStatusBarHeaderController(
view,
- privacyItemController,
- activityStarter,
- uiEventLogger,
+ privacyIconsController,
statusBarIconController,
demoModeController,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
- privacyLogger,
colorExtractor,
- privacyDialogController,
qsExpansionPathInterpolator,
featureFlags,
variableDateViewControllerFactory,
@@ -169,62 +138,6 @@
}
@Test
- fun testIgnoredSlotsOnAttached_noIndicators() {
- setPrivacyController(micCamera = false, location = false)
-
- controller.init()
-
- verify(iconContainer).removeIgnoredSlot(cameraSlotName)
- verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
- verify(iconContainer).removeIgnoredSlot(locationSlotName)
- }
-
- @Test
- fun testIgnoredSlotsOnAttached_onlyMicCamera() {
- setPrivacyController(micCamera = true, location = false)
-
- controller.init()
-
- verify(iconContainer).addIgnoredSlot(cameraSlotName)
- verify(iconContainer).addIgnoredSlot(microphoneSlotName)
- verify(iconContainer).removeIgnoredSlot(locationSlotName)
- }
-
- @Test
- fun testIgnoredSlotsOnAttached_onlyLocation() {
- setPrivacyController(micCamera = false, location = true)
-
- controller.init()
-
- verify(iconContainer).removeIgnoredSlot(cameraSlotName)
- verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
- verify(iconContainer).addIgnoredSlot(locationSlotName)
- }
-
- @Test
- fun testIgnoredSlotsOnAttached_locationMicCamera() {
- setPrivacyController(micCamera = true, location = true)
-
- controller.init()
-
- verify(iconContainer).addIgnoredSlot(cameraSlotName)
- verify(iconContainer).addIgnoredSlot(microphoneSlotName)
- verify(iconContainer).addIgnoredSlot(locationSlotName)
- }
-
- @Test
- fun testPrivacyChipClicked() {
- controller.init()
-
- val captor = argumentCaptor<View.OnClickListener>()
- verify(privacyChip).setOnClickListener(capture(captor))
-
- captor.value.onClick(privacyChip)
-
- verify(privacyDialogController).showDialog(any(Context::class.java))
- }
-
- @Test
fun testSingleCarrierListenerAttachedOnInit() {
controller.init()
@@ -293,14 +206,8 @@
`when`(view.findViewById<View>(anyInt())).thenReturn(mockView)
`when`(view.findViewById<QSCarrierGroup>(R.id.carrier_group)).thenReturn(qsCarrierGroup)
`when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer)
- `when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip)
`when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
`when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView)
`when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView)
}
-
- private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
- `when`(privacyItemController.micCameraAvailable).thenReturn(micCamera)
- `when`(privacyItemController.locationAvailable).thenReturn(location)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
new file mode 100644
index 0000000..64796f1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.qs.external
+
+import android.app.StatusBarManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.InstanceIdSequenceFake
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class TileRequestDialogEventLoggerTest : SysuiTestCase() {
+
+ companion object {
+ private const val PACKAGE_NAME = "package"
+ }
+
+ private lateinit var uiEventLogger: UiEventLoggerFake
+ private val instanceIdSequence =
+ InstanceIdSequenceFake(TileRequestDialogEventLogger.MAX_INSTANCE_ID)
+ private lateinit var logger: TileRequestDialogEventLogger
+
+ @Before
+ fun setUp() {
+ uiEventLogger = UiEventLoggerFake()
+
+ logger = TileRequestDialogEventLogger(uiEventLogger, instanceIdSequence)
+ }
+
+ @Test
+ fun testInstanceIdsFromSequence() {
+ (1..10).forEach {
+ assertThat(logger.newInstanceId().id).isEqualTo(instanceIdSequence.lastInstanceId)
+ }
+ }
+
+ @Test
+ fun testLogTileAlreadyAdded() {
+ val instanceId = instanceIdSequence.newInstanceId()
+ logger.logTileAlreadyAdded(PACKAGE_NAME, instanceId)
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(
+ TileRequestDialogEvent.TILE_REQUEST_DIALOG_TILE_ALREADY_ADDED,
+ instanceId
+ )
+ }
+
+ @Test
+ fun testLogDialogShown() {
+ val instanceId = instanceIdSequence.newInstanceId()
+ logger.logDialogShown(PACKAGE_NAME, instanceId)
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(TileRequestDialogEvent.TILE_REQUEST_DIALOG_SHOWN, instanceId)
+ }
+
+ @Test
+ fun testLogDialogDismissed() {
+ val instanceId = instanceIdSequence.newInstanceId()
+ logger.logUserResponse(
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED,
+ PACKAGE_NAME,
+ instanceId
+ )
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(TileRequestDialogEvent.TILE_REQUEST_DIALOG_DISMISSED, instanceId)
+ }
+
+ @Test
+ fun testLogDialogTileNotAdded() {
+ val instanceId = instanceIdSequence.newInstanceId()
+ logger.logUserResponse(
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED,
+ PACKAGE_NAME,
+ instanceId
+ )
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0]
+ .match(TileRequestDialogEvent.TILE_REQUEST_DIALOG_TILE_NOT_ADDED, instanceId)
+ }
+
+ @Test
+ fun testLogDialogTileAdded() {
+ val instanceId = instanceIdSequence.newInstanceId()
+ logger.logUserResponse(
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED,
+ PACKAGE_NAME,
+ instanceId
+ )
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(TileRequestDialogEvent.TILE_REQUEST_DIALOG_TILE_ADDED, instanceId)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testLogResponseInvalid_throws() {
+ val instanceId = instanceIdSequence.newInstanceId()
+ logger.logUserResponse(
+ -1,
+ PACKAGE_NAME,
+ instanceId
+ )
+ }
+
+ private fun UiEventLoggerFake.FakeUiEvent.match(
+ event: UiEventLogger.UiEventEnum,
+ instanceId: InstanceId
+ ) {
+ assertThat(eventId).isEqualTo(event.id)
+ assertThat(uid).isEqualTo(0)
+ assertThat(packageName).isEqualTo(PACKAGE_NAME)
+ assertThat(this.instanceId).isEqualTo(instanceId)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
index d49673d..f56a185 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
@@ -60,11 +60,6 @@
}
@Test
- fun useCorrectTheme() {
- assertThat(dialog.context.themeResId).isEqualTo(R.style.TileRequestDialog)
- }
-
- @Test
fun setTileData_hasCorrectViews() {
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
index 70e971c..a1c60a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
@@ -16,18 +16,22 @@
package com.android.systemui.qs.external
+import android.app.StatusBarManager
import android.content.ComponentName
import android.content.DialogInterface
import android.graphics.drawable.Icon
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
import com.android.internal.statusbar.IAddTileResultCallback
+import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.QSTileHost
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -63,18 +67,28 @@
@Mock
private lateinit var commandQueue: CommandQueue
@Mock
+ private lateinit var logger: TileRequestDialogEventLogger
+ @Mock
private lateinit var icon: Icon
+ private val instanceIdSequence = InstanceIdSequenceFake(1_000)
private lateinit var controller: TileServiceRequestController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ `when`(logger.newInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
+
// Tile not present by default
`when`(qsTileHost.indexOf(anyString())).thenReturn(-1)
- controller = TileServiceRequestController(qsTileHost, commandQueue, commandRegistry) {
+ controller = TileServiceRequestController(
+ qsTileHost,
+ commandQueue,
+ commandRegistry,
+ logger
+ ) {
tileRequestDialog
}
@@ -102,6 +116,17 @@
}
@Test
+ fun tileAlreadyAdded_logged() {
+ `when`(qsTileHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
+
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+
+ verify(logger).logTileAlreadyAdded(eq<String>(TEST_COMPONENT.packageName), any())
+ verify(logger, never()).logDialogShown(anyString(), any())
+ verify(logger, never()).logUserResponse(anyInt(), anyString(), any())
+ }
+
+ @Test
fun showAllUsers_set() {
controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
verify(tileRequestDialog).setShowForAllUsers(true)
@@ -114,6 +139,13 @@
}
@Test
+ fun dialogShown_logged() {
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+
+ verify(logger).logDialogShown(eq<String>(TEST_COMPONENT.packageName), any())
+ }
+
+ @Test
fun cancelListener_dismissResult() {
val cancelListenerCaptor =
ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java)
@@ -128,6 +160,25 @@
}
@Test
+ fun dialogCancelled_logged() {
+ val cancelListenerCaptor =
+ ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java)
+
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+ val instanceId = InstanceId.fakeInstanceId(instanceIdSequence.lastInstanceId)
+
+ verify(tileRequestDialog).setOnCancelListener(capture(cancelListenerCaptor))
+ verify(logger).logDialogShown(TEST_COMPONENT.packageName, instanceId)
+
+ cancelListenerCaptor.value.onCancel(tileRequestDialog)
+ verify(logger).logUserResponse(
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED,
+ TEST_COMPONENT.packageName,
+ instanceId
+ )
+ }
+
+ @Test
fun positiveActionListener_tileAddedResult() {
val clickListenerCaptor =
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
@@ -143,6 +194,25 @@
}
@Test
+ fun tileAdded_logged() {
+ val clickListenerCaptor =
+ ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
+
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+ val instanceId = InstanceId.fakeInstanceId(instanceIdSequence.lastInstanceId)
+
+ verify(tileRequestDialog).setPositiveButton(anyInt(), capture(clickListenerCaptor))
+ verify(logger).logDialogShown(TEST_COMPONENT.packageName, instanceId)
+
+ clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_POSITIVE)
+ verify(logger).logUserResponse(
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED,
+ TEST_COMPONENT.packageName,
+ instanceId
+ )
+ }
+
+ @Test
fun negativeActionListener_tileNotAddedResult() {
val clickListenerCaptor =
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
@@ -158,6 +228,25 @@
}
@Test
+ fun tileNotAdded_logged() {
+ val clickListenerCaptor =
+ ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
+
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
+ val instanceId = InstanceId.fakeInstanceId(instanceIdSequence.lastInstanceId)
+
+ verify(tileRequestDialog).setNegativeButton(anyInt(), capture(clickListenerCaptor))
+ verify(logger).logDialogShown(TEST_COMPONENT.packageName, instanceId)
+
+ clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_NEGATIVE)
+ verify(logger).logUserResponse(
+ StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED,
+ TEST_COMPONENT.packageName,
+ instanceId
+ )
+ }
+
+ @Test
fun commandQueueCallback_registered() {
verify(commandQueue).addCallback(any())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 5a49337..b40a20c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -38,6 +38,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -90,6 +91,8 @@
private HotspotController.Callback mHotspotCallback;
@Mock
private QSLogger mQSLogger;
+ @Mock
+ private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -113,7 +116,8 @@
mController,
mKeyguard,
mNetworkController,
- mHotspotController
+ mHotspotController,
+ mDialogLaunchAnimator
);
mCastTile.initialize();
@@ -241,6 +245,7 @@
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
+ when(mKeyguard.isShowing()).thenReturn(true);
enableWifiAndProcessMessages();
mCastTile.handleClick(null /* view */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index f99703e..9936d49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -16,22 +16,28 @@
package com.android.systemui.qs.tiles
+import android.app.Dialog
import android.content.ContextWrapper
import android.content.SharedPreferences
import android.os.Handler
import android.provider.Settings
+import android.provider.Settings.Global.ZEN_MODE_OFF
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
@@ -40,9 +46,12 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.io.File
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -70,6 +79,10 @@
private lateinit var zenModeController: ZenModeController
@Mock
private lateinit var sharedPreferences: SharedPreferences
+ @Mock
+ private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock
+ private lateinit var hostDialog: Dialog
private lateinit var secureSettings: SecureSettings
private lateinit var testableLooper: TestableLooper
@@ -81,15 +94,15 @@
testableLooper = TestableLooper.get(this)
secureSettings = FakeSettings()
- Mockito.`when`(qsHost.userId).thenReturn(DEFAULT_USER)
- Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+ whenever(qsHost.userId).thenReturn(DEFAULT_USER)
+ whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger)
val wrappedContext = object : ContextWrapper(context) {
override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
return sharedPreferences
}
}
- Mockito.`when`(qsHost.context).thenReturn(wrappedContext)
+ whenever(qsHost.context).thenReturn(wrappedContext)
tile = DndTile(
qsHost,
@@ -102,7 +115,8 @@
qsLogger,
zenModeController,
sharedPreferences,
- secureSettings
+ secureSettings,
+ dialogLaunchAnimator
)
}
@@ -147,4 +161,32 @@
assertThat(tile.state.forceExpandIcon).isTrue()
}
+
+ @Test
+ fun testLaunchDialogFromViewWhenPrompt() {
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+
+ secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
+ testableLooper.processAllMessages()
+
+ val view = View(context)
+ tile.handleClick(view)
+ testableLooper.processAllMessages()
+
+ verify(dialogLaunchAnimator).showFromView(any(), eq(view), anyBoolean())
+ }
+
+ @Test
+ fun testNoLaunchDialogWhenNotPrompt() {
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+
+ secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
+ testableLooper.processAllMessages()
+
+ val view = View(context)
+ tile.handleClick(view)
+ testableLooper.processAllMessages()
+
+ verify(dialogLaunchAnimator, never()).showFromView(any(), any(), anyBoolean())
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index b32b4d4..339d5bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -219,7 +221,7 @@
}
@Test
- public void updateDialog_wifiOnAndNoWifiEntry_hideWifiEntryAndSeeAll() {
+ public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
// The precondition WiFi ON is already in setUp()
mInternetDialog.mConnectedWifiEntry = null;
mInternetDialog.mWifiEntriesCount = 0;
@@ -227,19 +229,21 @@
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ // Show a blank block to fix the dialog height even if there is no WiFi list
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
}
@Test
- public void updateDialog_wifiOnAndHasConnectedWifi_showConnectedWifiAndSeeAll() {
+ public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
mInternetDialog.mWifiEntriesCount = 0;
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ // Show a blank block to fix the dialog height even if there is no WiFi list
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -412,4 +416,54 @@
assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
assertThat(mInternetDialog.mIsSearchingHidden).isTrue();
}
+
+ @Test
+ public void getWifiListMaxCount_returnCountCorrectly() {
+ // Ethernet, MobileData, ConnectedWiFi are all hidden.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+ setNetworkVisible(false, false, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+
+ // Only one of Ethernet, MobileData, ConnectedWiFi is displayed.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
+ setNetworkVisible(true, false, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ setNetworkVisible(false, true, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ setNetworkVisible(false, false, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ // Only one of Ethernet, MobileData, ConnectedWiFi is hidden.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 2.
+ setNetworkVisible(true, true, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+ setNetworkVisible(true, false, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+ setNetworkVisible(false, true, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+ // Ethernet, MobileData, ConnectedWiFi are all displayed.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 3.
+ setNetworkVisible(true, true, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 3);
+ }
+
+ private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
+ boolean connectedWifiVisible) {
+ mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
+ mMobileDataToggle.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
+ mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
deleted file mode 100644
index d5fe588..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
+++ /dev/null
@@ -1,62 +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.qs.user
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.View
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class UserDialogTest : SysuiTestCase() {
-
- private lateinit var dialog: UserDialog
-
- @Before
- fun setUp() {
- dialog = UserDialog(mContext)
- }
-
- @After
- fun tearDown() {
- dialog.dismiss()
- }
-
- @Test
- fun doneButtonExists() {
- assertThat(dialog.doneButton).isInstanceOf(View::class.java)
- }
-
- @Test
- fun settingsButtonExists() {
- assertThat(dialog.settingsButton).isInstanceOf(View::class.java)
- }
-
- @Test
- fun gridExistsAndIsViewGroup() {
- assertThat(dialog.grid).isInstanceOf(ViewGroup::class.java)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index ea3a42c..b7fdc1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.user
-import android.app.Dialog
+import android.content.DialogInterface
import android.content.Intent
import android.provider.Settings
import android.testing.AndroidTestingRunner
@@ -28,8 +28,8 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PseudoGridView
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.phone.SystemUIDialog
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 org.junit.Before
@@ -40,10 +40,8 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.argThat
-import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -53,27 +51,19 @@
class UserSwitchDialogControllerTest : SysuiTestCase() {
@Mock
- private lateinit var dialog: UserDialog
+ private lateinit var dialog: SystemUIDialog
@Mock
private lateinit var falsingManager: FalsingManager
@Mock
- private lateinit var settingsView: View
- @Mock
- private lateinit var doneView: View
- @Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var userDetailViewAdapter: UserDetailView.Adapter
@Mock
private lateinit var launchView: View
@Mock
- private lateinit var gridView: PseudoGridView
- @Mock
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
- @Mock
- private lateinit var hostDialog: Dialog
@Captor
- private lateinit var clickCaptor: ArgumentCaptor<View.OnClickListener>
+ private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
private lateinit var controller: UserSwitchDialogController
@@ -81,13 +71,8 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(dialog.settingsButton).thenReturn(settingsView)
- `when`(dialog.doneButton).thenReturn(doneView)
- `when`(dialog.grid).thenReturn(gridView)
-
`when`(launchView.context).thenReturn(mContext)
- `when`(dialogLaunchAnimator.showFromView(any(), any(), anyBoolean()))
- .thenReturn(hostDialog)
+ `when`(dialog.context).thenReturn(mContext)
controller = UserSwitchDialogController(
{ userDetailViewAdapter },
@@ -105,30 +90,6 @@
}
@Test
- fun createCalledBeforeDoneButton() {
- controller.showDialog(launchView)
- val inOrder = inOrder(dialog)
- inOrder.verify(dialog).create()
- inOrder.verify(dialog).doneButton
- }
-
- @Test
- fun createCalledBeforeSettingsButton() {
- controller.showDialog(launchView)
- val inOrder = inOrder(dialog)
- inOrder.verify(dialog).create()
- inOrder.verify(dialog).settingsButton
- }
-
- @Test
- fun createCalledBeforeGrid() {
- controller.showDialog(launchView)
- val inOrder = inOrder(dialog)
- inOrder.verify(dialog).create()
- inOrder.verify(dialog).grid
- }
-
- @Test
fun dialog_showForAllUsers() {
controller.showDialog(launchView)
verify(dialog).setShowForAllUsers(true)
@@ -143,63 +104,44 @@
@Test
fun adapterAndGridLinked() {
controller.showDialog(launchView)
- verify(userDetailViewAdapter).linkToViewGroup(gridView)
+ verify(userDetailViewAdapter).linkToViewGroup(any<PseudoGridView>())
}
@Test
- fun clickDoneButton_dismiss() {
+ fun doneButtonSetWithNullHandler() {
controller.showDialog(launchView)
- verify(doneView).setOnClickListener(capture(clickCaptor))
-
- clickCaptor.value.onClick(doneView)
-
- verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
- verify(dialog).dismiss()
+ verify(dialog).setPositiveButton(anyInt(), eq(null))
}
@Test
- fun clickSettingsButton_noFalsing_opensSettingsAndDismisses() {
+ fun clickSettingsButton_noFalsing_opensSettings() {
`when`(falsingManager.isFalseTap(anyInt())).thenReturn(false)
controller.showDialog(launchView)
- verify(settingsView).setOnClickListener(capture(clickCaptor))
+ verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
- clickCaptor.value.onClick(settingsView)
+ clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
verify(activityStarter)
.postStartActivityDismissingKeyguard(
argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
eq(0)
)
- verify(dialog).dismiss()
}
@Test
- fun clickSettingsButton_Falsing_notOpensSettingsAndDismisses() {
+ fun clickSettingsButton_Falsing_notOpensSettings() {
`when`(falsingManager.isFalseTap(anyInt())).thenReturn(true)
controller.showDialog(launchView)
- verify(settingsView).setOnClickListener(capture(clickCaptor))
+ verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
- clickCaptor.value.onClick(settingsView)
+ clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
- verify(dialog).dismiss()
- }
-
- @Test
- fun callbackFromDialogShower_dismissesDialog() {
- val captor = argumentCaptor<UserSwitchDialogController.DialogShower>()
-
- controller.showDialog(launchView)
- verify(userDetailViewAdapter).injectDialogShower(capture(captor))
-
- captor.value.dismiss()
-
- verify(hostDialog).dismiss()
}
private class IntentMatcher(private val action: String) : ArgumentMatcher<Intent> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index b0f2a89..4213b07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -29,6 +29,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
@@ -54,6 +55,7 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NonPhoneDependencyTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
+ @Mock private NotifStackController mStackController;
@Mock private NotificationListContainer mListContainer;
@Mock
private NotificationEntryListener mEntryListener;
@@ -95,7 +97,7 @@
remoteInputManager.setUpWithCallback(mRemoteInputManagerCallback,
mDelegate);
lockscreenUserManager.setUpWithPresenter(mPresenter);
- viewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
+ viewHierarchyManager.setUpWithPresenter(mPresenter, mStackController, mListContainer);
TestableLooper.get(this).processAllMessages();
assertFalse(mDependency.hasInstantiatedDependency(NotificationShadeWindowController.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 3972f14..83f1d87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -74,6 +75,7 @@
@TestableLooper.RunWithLooper
public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
+ @Mock private NotifStackController mStackController;
@Spy private FakeListContainer mListContainer = new FakeListContainer();
// Dependency mocks:
@@ -122,7 +124,7 @@
mock(LowPriorityInflationHelper.class),
mock(AssistantFeedbackController.class),
mNotifPipelineFlags);
- mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
+ mViewHierarchyManager.setUpWithPresenter(mPresenter, mStackController, mListContainer);
}
private NotificationEntry createEntry() throws Exception {
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 c974882..b736f38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -19,6 +19,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -26,20 +27,35 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class StatusBarStateControllerImplTest : SysuiTestCase() {
+ @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+
private lateinit var controller: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(interactionJankMonitor.begin(any(), anyInt())).thenReturn(true)
+ whenever(interactionJankMonitor.end(anyInt())).thenReturn(true)
+
uiEventLogger = UiEventLoggerFake()
- controller = StatusBarStateControllerImpl(uiEventLogger, mock(DumpManager::class.java))
+ controller = StatusBarStateControllerImpl(
+ uiEventLogger,
+ mock(DumpManager::class.java),
+ interactionJankMonitor
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
index e16d4d7..fda8f51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
@@ -32,6 +32,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.app.Notification;
@@ -43,7 +45,6 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Pair;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.SysuiTestCase;
@@ -51,8 +52,6 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.util.DeviceConfigProxyFake;
-import junit.framework.Assert;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -97,9 +96,24 @@
@Test
public void testFeedback_flagDisabled() {
switchFlag("false");
+ // test flag disables logic with default values
assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus(
getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
- assertFalse(mAssistantFeedbackController.showFeedbackIndicator(
+ assertNull(mAssistantFeedbackController.getFeedbackIcon(
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
+ // test that the flag disables logic with values that otherwise would return a value
+ assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus(
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED)));
+ assertNull(mAssistantFeedbackController.getFeedbackIcon(
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED)));
+ }
+
+ @Test
+ public void testFeedback_noChange() {
+ switchFlag("true");
+ assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus(
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
+ assertNull(mAssistantFeedbackController.getFeedbackIcon(
getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
}
@@ -108,15 +122,15 @@
switchFlag("true");
NotificationEntry entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_UNCHANGED);
assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertTrue(mAssistantFeedbackController.showFeedbackIndicator(entry));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_LOW, RANKING_UNCHANGED);
assertEquals(STATUS_SILENCED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertTrue(mAssistantFeedbackController.showFeedbackIndicator(entry));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
entry = getEntry(IMPORTANCE_LOW, IMPORTANCE_MIN, RANKING_UNCHANGED);
assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertTrue(mAssistantFeedbackController.showFeedbackIndicator(entry));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
}
@Test
@@ -125,18 +139,20 @@
NotificationEntry entry =
getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_PROMOTED);
assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertTrue(mAssistantFeedbackController.showFeedbackIndicator(entry));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_DEMOTED);
assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertTrue(mAssistantFeedbackController.showFeedbackIndicator(entry));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
}
@Test
- public void testGetFeedbackResources_flagDisabled() {
- switchFlag("false");
- Assert.assertEquals(new Pair(0, 0), mAssistantFeedbackController.getFeedbackResources(
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
+ public void testGetFeedbackIcon_whenPromoted() {
+ switchFlag("true");
+ FeedbackIcon expected = new FeedbackIcon(com.android.internal.R.drawable.ic_feedback_uprank,
+ com.android.internal.R.string.notification_feedback_indicator_promoted);
+ assertEquals(expected, mAssistantFeedbackController.getFeedbackIcon(
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED)));
}
private NotificationEntry getEntry(int oldImportance, int newImportance,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 8e6bcb01..41163bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -174,6 +174,41 @@
}
@Test
+ public void testGetGroupSummary() {
+ assertEquals(null, mCollection.getGroupSummary("group"));
+ NotifEvent summary = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setGroup(mContext, "group")
+ .setGroupSummary(mContext, true));
+
+ final NotificationEntry entry = mCollection.getGroupSummary("group");
+ assertEquals(summary.key, entry.getKey());
+ assertEquals(summary.sbn, entry.getSbn());
+ assertEquals(summary.ranking, entry.getRanking());
+ }
+
+ @Test
+ public void testIsOnlyChildInGroup() {
+ NotifEvent notif1 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1)
+ .setGroup(mContext, "group"));
+ final NotificationEntry entry = mCollection.getEntry(notif1.key);
+ assertTrue(mCollection.isOnlyChildInGroup(entry));
+
+ // summaries are not counted
+ mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setGroup(mContext, "group")
+ .setGroupSummary(mContext, true));
+ assertTrue(mCollection.isOnlyChildInGroup(entry));
+
+ mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 2)
+ .setGroup(mContext, "group"));
+ assertFalse(mCollection.isOnlyChildInGroup(entry));
+ }
+
+ @Test
public void testEventDispatchedWhenNotifPosted() {
// WHEN a notification is posted
NotifEvent notif1 = mNoMan.postNotif(
@@ -193,6 +228,15 @@
}
@Test
+ public void testCancelNonExistingNotification() {
+ NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88, "barTag"));
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+ mCollection.dismissNotification(entry, defaultStats(entry));
+ mCollection.dismissNotification(entry, defaultStats(entry));
+ mCollection.dismissNotification(entry, defaultStats(entry));
+ }
+
+ @Test
public void testEventDispatchedWhenNotifBatchPosted() {
// GIVEN a NotifCollection with one notif already posted
mNoMan.postNotif(buildNotif(TEST_PACKAGE, 2)
@@ -649,21 +693,6 @@
// THEN an exception is thrown
}
- @Test(expected = IllegalStateException.class)
- public void testDismissingNonExistentNotificationThrows() {
- // GIVEN a collection that originally had three notifs, but where one was dismissed
- NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
- NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
- NotifEvent notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 99));
- NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
- mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
-
- // WHEN we try to dismiss a notification that isn't present
- mCollection.dismissNotification(entry2, defaultStats(entry2));
-
- // THEN an exception is thrown
- }
-
@Test
public void testGroupChildrenAreDismissedLocallyWhenSummaryIsDismissed() {
// GIVEN a collection with two grouped notifs in it
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
index cf7174e..287f50c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
@@ -19,6 +19,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.render.RenderStageManager
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -33,12 +34,13 @@
@Mock private lateinit var notifCollection: NotifCollection
@Mock private lateinit var shadeListBuilder: ShadeListBuilder
+ @Mock private lateinit var renderStageManager: RenderStageManager
private lateinit var notifPipeline: NotifPipeline
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- notifPipeline = NotifPipeline(notifCollection, shadeListBuilder)
+ notifPipeline = NotifPipeline(notifCollection, shadeListBuilder, renderStageManager)
whenever(shadeListBuilder.shadeList).thenReturn(listOf(
NotificationEntryBuilder().setPkg("foo").setId(1).build(),
NotificationEntryBuilder().setPkg("foo").setId(2).build(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index b254ed4..82cd9fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -32,7 +32,6 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -52,6 +51,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.NotificationInteractionTracker;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
@@ -96,7 +96,9 @@
private ShadeListBuilder mListBuilder;
private FakeSystemClock mSystemClock = new FakeSystemClock();
+ @Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private ShadeListBuilderLogger mLogger;
+ @Mock private DumpManager mDumpManager;
@Mock private NotifCollection mNotifCollection;
@Mock private NotificationInteractionTracker mInteractionTracker;
@Spy private OnBeforeTransformGroupsListener mOnBeforeTransformGroupsListener;
@@ -122,7 +124,12 @@
allowTestableLooperAsMainThread();
mListBuilder = new ShadeListBuilder(
- mSystemClock, mLogger, mock(DumpManager.class), mInteractionTracker);
+ mSystemClock,
+ mNotifPipelineFlags,
+ mLogger,
+ mDumpManager,
+ mInteractionTracker
+ );
mListBuilder.setOnRenderListListener(mOnRenderListListener);
mListBuilder.attach(mNotifCollection);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
index 01e4cce0..f4452bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.test.suitebuilder.annotation.SmallTest;
@@ -29,6 +30,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -39,6 +42,8 @@
@SmallTest
public class CommunalCoordinatorTest extends SysuiTestCase {
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
@Mock
CommunalStateController mCommunalStateController;
@Mock
@@ -57,7 +62,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mCoordinator = new CommunalCoordinator(mNotificationEntryManager,
+ mCoordinator = new CommunalCoordinator(mExecutor, mNotificationEntryManager,
mNotificationLockscreenUserManager, mCommunalStateController);
}
@@ -84,6 +89,12 @@
// Verify that notifications are filtered out when communal is showing and that the filter
// pipeline is notified.
stateCallback.onCommunalViewShowingChanged();
+ // Make sure callback depends on executor to run.
+ verify(mFilterListener, never()).onPluggableInvalidated(any());
+ verify(mNotificationEntryManager, never()).updateNotifications(any());
+
+ mExecutor.runAllReady();
+
verify(mFilterListener).onPluggableInvalidated(any());
verify(mNotificationEntryManager).updateNotifications(any());
assert (filter.shouldFilterOut(mNotificationEntry, 0));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
new file mode 100644
index 0000000..929c3d4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
+import com.android.systemui.statusbar.notification.collection.render.NotifGroupController
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.withArgCaptor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class GroupCountCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: GroupCountCoordinator
+ private lateinit var beforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
+ private lateinit var afterRenderGroupListener: OnAfterRenderGroupListener
+
+ private lateinit var summaryEntry: NotificationEntry
+ private lateinit var childEntry1: NotificationEntry
+ private lateinit var childEntry2: NotificationEntry
+
+ @Mock private lateinit var pipeline: NotifPipeline
+ @Mock private lateinit var groupController: NotifGroupController
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ coordinator = GroupCountCoordinator()
+ coordinator.attach(pipeline)
+ beforeFinalizeFilterListener = withArgCaptor {
+ verify(pipeline).addOnBeforeFinalizeFilterListener(capture())
+ }
+ afterRenderGroupListener = withArgCaptor {
+ verify(pipeline).addOnAfterRenderGroupListener(capture())
+ }
+ summaryEntry = NotificationEntryBuilder().setId(0).build()
+ childEntry1 = NotificationEntryBuilder().setId(1).build()
+ childEntry2 = NotificationEntryBuilder().setId(2).build()
+ }
+
+ @Test
+ fun testSetUntruncatedChildCount() {
+ val groupEntry = GroupEntryBuilder()
+ .setSummary(summaryEntry)
+ .setChildren(listOf(childEntry1, childEntry2))
+ .build()
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+ verify(groupController).setUntruncatedChildCount(eq(2))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index c3e10aa..f70330d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -40,6 +40,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.SectionClassifier;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -85,7 +86,6 @@
@Captor private ArgumentCaptor<NotifCollectionListener> mCollectionListenerCaptor;
@Captor private ArgumentCaptor<OnBeforeFinalizeFilterListener> mBeforeFilterListenerCaptor;
- @Captor private ArgumentCaptor<NotifInflater.InflationCallback> mCallbackCaptor;
@Captor private ArgumentCaptor<NotifInflater.Params> mParamsCaptor;
@Mock private NotifSectioner mNotifSectioner;
@@ -93,7 +93,9 @@
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService;
@Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
- private final TestableAdjustmentProvider mAdjustmentProvider = new TestableAdjustmentProvider();
+ private final SectionClassifier mSectionClassifier = new SectionClassifier();
+ private final NotifUiAdjustmentProvider mAdjustmentProvider =
+ new NotifUiAdjustmentProvider(mSectionClassifier);
@NonNull
private NotificationEntryBuilder getNotificationEntryBuilder() {
@@ -108,7 +110,7 @@
mInflationError = new Exception(TEST_MESSAGE);
mErrorManager = new NotifInflationErrorManager();
when(mNotifSection.getSectioner()).thenReturn(mNotifSectioner);
- mAdjustmentProvider.setSectionIsLowPriority(false);
+ setSectionIsLowPriority(false);
PreparationCoordinator coordinator = new PreparationCoordinator(
mock(PreparationCoordinatorLogger.class),
@@ -180,8 +182,8 @@
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
- mCallbackCaptor.getValue().onInflationFinished(mEntry);
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), any());
+ mNotifInflater.invokeInflateCallbackForEntry(mEntry);
// WHEN notification is updated
mCollectionListener.onEntryUpdated(mEntry);
@@ -199,8 +201,8 @@
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
- mCallbackCaptor.getValue().onInflationFinished(mEntry);
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), any());
+ mNotifInflater.invokeInflateCallbackForEntry(mEntry);
// WHEN notification ranking now has smart replies
mEntry.setRanking(new RankingBuilder(mEntry.getRanking()).setSmartReplies("yes").build());
@@ -218,13 +220,12 @@
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry),
- mParamsCaptor.capture(), mCallbackCaptor.capture());
+ verify(mNotifInflater).inflateViews(eq(mEntry), mParamsCaptor.capture(), any());
assertFalse(mParamsCaptor.getValue().isLowPriority());
- mCallbackCaptor.getValue().onInflationFinished(mEntry);
+ mNotifInflater.invokeInflateCallbackForEntry(mEntry);
// WHEN notification moves to a min priority section
- mAdjustmentProvider.setSectionIsLowPriority(true);
+ setSectionIsLowPriority(true);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
// THEN we rebind it
@@ -238,13 +239,12 @@
@Test
public void testMinimizedEntryMovedIntoGroupWillRebindViews() {
// GIVEN an inflated, minimized notification
- mAdjustmentProvider.setSectionIsLowPriority(true);
+ setSectionIsLowPriority(true);
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry),
- mParamsCaptor.capture(), mCallbackCaptor.capture());
+ verify(mNotifInflater).inflateViews(eq(mEntry), mParamsCaptor.capture(), any());
assertTrue(mParamsCaptor.getValue().isLowPriority());
- mCallbackCaptor.getValue().onInflationFinished(mEntry);
+ mNotifInflater.invokeInflateCallbackForEntry(mEntry);
// WHEN notification is moved under a parent
NotificationEntryBuilder.setNewParent(mEntry, mock(GroupEntry.class));
@@ -263,8 +263,8 @@
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
- mCallbackCaptor.getValue().onInflationFinished(mEntry);
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), any());
+ mNotifInflater.invokeInflateCallbackForEntry(mEntry);
// WHEN notification ranking changes rank, which does not affect views
mEntry.setRanking(new RankingBuilder(mEntry.getRanking()).setRank(100).build());
@@ -282,8 +282,8 @@
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
- mCallbackCaptor.getValue().onInflationFinished(mEntry);
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), any());
+ mNotifInflater.invokeInflateCallbackForEntry(mEntry);
// THEN it isn't filtered from shade list
assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
@@ -347,7 +347,7 @@
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(group));
// WHEN one of this children finishes inflating
- mNotifInflater.getInflateCallback(child0).onInflationFinished(child0);
+ mNotifInflater.invokeInflateCallbackForEntry(child0);
// THEN the inflated child is still filtered out
assertTrue(mUninflatedFilter.shouldFilterOut(child0, 401));
@@ -369,8 +369,8 @@
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(group));
// WHEN all of the children (but not the summary) finish inflating
- mNotifInflater.getInflateCallback(child0).onInflationFinished(child0);
- mNotifInflater.getInflateCallback(child1).onInflationFinished(child1);
+ mNotifInflater.invokeInflateCallbackForEntry(child0);
+ mNotifInflater.invokeInflateCallbackForEntry(child1);
// THEN the entire group is still filtered out
assertTrue(mUninflatedFilter.shouldFilterOut(summary, 401));
@@ -394,9 +394,9 @@
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(group));
// WHEN all of the children (and the summary) finish inflating
- mNotifInflater.getInflateCallback(child0).onInflationFinished(child0);
- mNotifInflater.getInflateCallback(child1).onInflationFinished(child1);
- mNotifInflater.getInflateCallback(summary).onInflationFinished(summary);
+ mNotifInflater.invokeInflateCallbackForEntry(child0);
+ mNotifInflater.invokeInflateCallbackForEntry(child1);
+ mNotifInflater.invokeInflateCallbackForEntry(summary);
// THEN the entire group is still filtered out
assertFalse(mUninflatedFilter.shouldFilterOut(summary, 401));
@@ -418,7 +418,7 @@
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(group));
// WHEN one of this children finishes inflating and enough time passes
- mNotifInflater.getInflateCallback(child0).onInflationFinished(child0);
+ mNotifInflater.invokeInflateCallbackForEntry(child0);
// THEN the inflated child is not filtered out even though the rest of the group hasn't
// finished inflating yet
@@ -446,6 +446,10 @@
public InflationCallback getInflateCallback(NotificationEntry entry) {
return requireNonNull(mInflateCallbacks.get(entry));
}
+
+ public void invokeInflateCallbackForEntry(NotificationEntry entry) {
+ getInflateCallback(entry).onInflationFinished(entry, entry.getRowController());
+ }
}
private void fireAddEvents(List<? extends ListEntry> entries) {
@@ -470,11 +474,9 @@
private static final int TEST_CHILD_BIND_CUTOFF = 9;
private static final int TEST_MAX_GROUP_DELAY = 100;
- private class TestableAdjustmentProvider extends NotifUiAdjustmentProvider {
- private void setSectionIsLowPriority(boolean lowPriority) {
- setLowPrioritySections(lowPriority
- ? Collections.singleton(mNotifSection.getSectioner())
- : Collections.emptyList());
- }
+ private void setSectionIsLowPriority(boolean minimized) {
+ mSectionClassifier.setMinimizedSections(minimized
+ ? Collections.singleton(mNotifSection.getSectioner())
+ : Collections.emptyList());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index abe33aa..f4d8405 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -39,11 +40,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.SectionClassifier;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -67,7 +68,7 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private HighPriorityProvider mHighPriorityProvider;
- @Mock private NotifUiAdjustmentProvider mAdjustmentProvider;
+ @Mock private SectionClassifier mSectionClassifier;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NodeController mAlertingHeaderController;
@Mock private NodeController mSilentNodeController;
@@ -91,7 +92,7 @@
mRankingCoordinator = new RankingCoordinator(
mStatusBarStateController,
mHighPriorityProvider,
- mAdjustmentProvider,
+ mSectionClassifier,
mAlertingHeaderController,
mSilentHeaderController,
mSilentNodeController);
@@ -99,6 +100,7 @@
mEntry.setRanking(getRankingForUnfilteredNotif().build());
mRankingCoordinator.attach(mNotifPipeline);
+ verify(mSectionClassifier).setMinimizedSections(any());
verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
new file mode 100644
index 0000000..447ba15
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.AssistantFeedbackController
+import com.android.systemui.statusbar.notification.FeedbackIcon
+import com.android.systemui.statusbar.notification.SectionClassifier
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
+import com.android.systemui.statusbar.notification.collection.render.NotifRowController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.withArgCaptor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class RowAppearanceCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: RowAppearanceCoordinator
+ private lateinit var beforeRenderListListener: OnBeforeRenderListListener
+ private lateinit var afterRenderEntryListener: OnAfterRenderEntryListener
+
+ private lateinit var entry1: NotificationEntry
+ private lateinit var entry2: NotificationEntry
+
+ @Mock private lateinit var pipeline: NotifPipeline
+ @Mock private lateinit var assistantFeedbackController: AssistantFeedbackController
+ @Mock private lateinit var sectionClassifier: SectionClassifier
+
+ @Mock private lateinit var section1: NotifSection
+ @Mock private lateinit var section2: NotifSection
+ @Mock private lateinit var controller1: NotifRowController
+ @Mock private lateinit var controller2: NotifRowController
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ coordinator = RowAppearanceCoordinator(
+ mContext,
+ assistantFeedbackController,
+ sectionClassifier
+ )
+ coordinator.attach(pipeline)
+ beforeRenderListListener = withArgCaptor {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+ afterRenderEntryListener = withArgCaptor {
+ verify(pipeline).addOnAfterRenderEntryListener(capture())
+ }
+ whenever(assistantFeedbackController.getFeedbackIcon(any())).thenReturn(FeedbackIcon(1, 2))
+ entry1 = NotificationEntryBuilder().setSection(section1).setLastAudiblyAlertedMs(17).build()
+ entry2 = NotificationEntryBuilder().setSection(section2).build()
+ }
+
+ @Test
+ fun testSetSystemExpandedOnlyOnFirst() {
+ whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(false)
+ whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(false)
+ beforeRenderListListener.onBeforeRenderList(listOf(entry1, entry2))
+ afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
+ verify(controller1).setSystemExpanded(eq(true))
+ afterRenderEntryListener.onAfterRenderEntry(entry2, controller2)
+ verify(controller2).setSystemExpanded(eq(false))
+ }
+
+ @Test
+ fun testSetSystemExpandedNeverIfMinimized() {
+ whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(true)
+ whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(true)
+ beforeRenderListListener.onBeforeRenderList(listOf(entry1, entry2))
+ afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
+ verify(controller1).setSystemExpanded(eq(false))
+ afterRenderEntryListener.onAfterRenderEntry(entry2, controller2)
+ verify(controller2).setSystemExpanded(eq(false))
+ }
+
+ @Test
+ fun testSetLastAudiblyAlerted() {
+ afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
+ verify(controller1).setLastAudiblyAlertedMs(eq(17.toLong()))
+ }
+
+ @Test
+ fun testSetFeedbackIcon() {
+ afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
+ verify(controller1).setFeedbackIcon(eq(FeedbackIcon(1, 2)))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
new file mode 100644
index 0000000..70266e4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
+import com.android.systemui.statusbar.notification.collection.render.NotifStackController
+import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
+import com.android.systemui.statusbar.phone.NotificationIconAreaController
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.withArgCaptor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class StackCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: StackCoordinator
+ private lateinit var afterRenderListListener: OnAfterRenderListListener
+
+ private lateinit var entry: NotificationEntry
+
+ @Mock private lateinit var pipeline: NotifPipeline
+ @Mock private lateinit var notificationIconAreaController: NotificationIconAreaController
+ @Mock private lateinit var stackController: NotifStackController
+ @Mock private lateinit var section: NotifSection
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ coordinator = StackCoordinator(notificationIconAreaController)
+ coordinator.attach(pipeline)
+ afterRenderListListener = withArgCaptor {
+ verify(pipeline).addOnAfterRenderListListener(capture())
+ }
+ entry = NotificationEntryBuilder().setSection(section).build()
+ }
+
+ @Test
+ fun testUpdateNotificationIcons() {
+ afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+ verify(notificationIconAreaController).updateNotificationIcons(eq(listOf(entry)))
+ }
+
+ @Test
+ fun testSetNotificationStats_clearableAlerting() {
+ whenever(section.bucket).thenReturn(BUCKET_ALERTING)
+ afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+ verify(stackController).setNotifStats(NotifStats(1, false, true, false, false))
+ }
+
+ @Test
+ fun testSetNotificationStats_clearableSilent() {
+ whenever(section.bucket).thenReturn(BUCKET_SILENT)
+ afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+ verify(stackController).setNotifStats(NotifStats(1, false, false, false, true))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index 6313d3a..5271745 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -68,7 +68,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(viewBarn.requireView(any())).thenAnswer {
+ `when`(viewBarn.requireNodeController(any())).thenAnswer {
fakeViewBarn.getViewByEntry(it.getArgument(0))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
new file mode 100644
index 0000000..70d309b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
@@ -0,0 +1,222 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class RenderStageManagerTest : SysuiTestCase() {
+
+ @Mock private lateinit var shadeListBuilder: ShadeListBuilder
+ @Mock private lateinit var onAfterRenderListListener: OnAfterRenderListListener
+ @Mock private lateinit var onAfterRenderGroupListener: OnAfterRenderGroupListener
+ @Mock private lateinit var onAfterRenderEntryListener: OnAfterRenderEntryListener
+
+ private lateinit var onRenderListListener: ShadeListBuilder.OnRenderListListener
+ private lateinit var renderStageManager: RenderStageManager
+ private val spyViewRenderer = spy(FakeNotifViewRenderer())
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ renderStageManager = RenderStageManager()
+ renderStageManager.attach(shadeListBuilder)
+ onRenderListListener = withArgCaptor {
+ verify(shadeListBuilder).setOnRenderListListener(capture())
+ }
+ }
+
+ private fun setUpRenderer() {
+ renderStageManager.setViewRenderer(spyViewRenderer)
+ }
+
+ private fun setUpListeners() {
+ renderStageManager.addOnAfterRenderListListener(onAfterRenderListListener)
+ renderStageManager.addOnAfterRenderGroupListener(onAfterRenderGroupListener)
+ renderStageManager.addOnAfterRenderEntryListener(onAfterRenderEntryListener)
+ }
+
+ @Test
+ fun testNoCallbacksWithoutRenderer() {
+ // GIVEN listeners but no renderer
+ setUpListeners()
+
+ // WHEN a shade list is built
+ onRenderListListener.onRenderList(listWith2Groups8Entries())
+
+ // VERIFY that no listeners are called
+ verifyNoMoreInteractions(
+ onAfterRenderListListener,
+ onAfterRenderGroupListener,
+ onAfterRenderEntryListener
+ )
+ }
+
+ @Test
+ fun testDoesNotQueryControllerIfNoListeners() {
+ // GIVEN a renderer but no listeners
+ setUpRenderer()
+
+ // WHEN a shade list is built
+ onRenderListListener.onRenderList(listWith2Groups8Entries())
+
+ // VERIFY that the renderer is not queried for group or row controllers
+ inOrder(spyViewRenderer).apply {
+ verify(spyViewRenderer, times(1)).onRenderList(any())
+ verify(spyViewRenderer, times(1)).getStackController()
+ verify(spyViewRenderer, never()).getGroupController(any())
+ verify(spyViewRenderer, never()).getRowController(any())
+ verify(spyViewRenderer, times(1)).onDispatchComplete()
+ verifyNoMoreInteractions(spyViewRenderer)
+ }
+ }
+
+ @Test
+ fun testDoesQueryControllerIfListeners() {
+ // GIVEN a renderer and listeners
+ setUpRenderer()
+ setUpListeners()
+
+ // WHEN a shade list is built
+ onRenderListListener.onRenderList(listWith2Groups8Entries())
+
+ // VERIFY that the renderer is queried once per group/entry
+ inOrder(spyViewRenderer).apply {
+ verify(spyViewRenderer, times(1)).onRenderList(any())
+ verify(spyViewRenderer, times(1)).getStackController()
+ verify(spyViewRenderer, times(2)).getGroupController(any())
+ verify(spyViewRenderer, times(8)).getRowController(any())
+ verify(spyViewRenderer, times(1)).onDispatchComplete()
+ verifyNoMoreInteractions(spyViewRenderer)
+ }
+ }
+
+ @Test
+ fun testDoesNotQueryControllerTwice() {
+ // GIVEN a renderer and multiple distinct listeners
+ setUpRenderer()
+ setUpListeners()
+ renderStageManager.addOnAfterRenderListListener(mock())
+ renderStageManager.addOnAfterRenderGroupListener(mock())
+ renderStageManager.addOnAfterRenderEntryListener(mock())
+
+ // WHEN a shade list is built
+ onRenderListListener.onRenderList(listWith2Groups8Entries())
+
+ // VERIFY that the renderer is queried once per group/entry
+ inOrder(spyViewRenderer).apply {
+ verify(spyViewRenderer, times(1)).onRenderList(any())
+ verify(spyViewRenderer, times(1)).getStackController()
+ verify(spyViewRenderer, times(2)).getGroupController(any())
+ verify(spyViewRenderer, times(8)).getRowController(any())
+ verify(spyViewRenderer, times(1)).onDispatchComplete()
+ verifyNoMoreInteractions(spyViewRenderer)
+ }
+ }
+
+ @Test
+ fun testDoesCallListenerWithEachGroupAndEntry() {
+ // GIVEN a renderer and multiple distinct listeners
+ setUpRenderer()
+ setUpListeners()
+
+ // WHEN a shade list is built
+ onRenderListListener.onRenderList(listWith2Groups8Entries())
+
+ // VERIFY that the listeners are invoked once per group and once per entry
+ verify(onAfterRenderListListener, times(1)).onAfterRenderList(any(), any())
+ verify(onAfterRenderGroupListener, times(2)).onAfterRenderGroup(any(), any())
+ verify(onAfterRenderEntryListener, times(8)).onAfterRenderEntry(any(), any())
+ verifyNoMoreInteractions(
+ onAfterRenderListListener,
+ onAfterRenderGroupListener,
+ onAfterRenderEntryListener
+ )
+ }
+
+ @Test
+ fun testDoesNotCallGroupAndEntryListenersIfTheListIsEmpty() {
+ // GIVEN a renderer and multiple distinct listeners
+ setUpRenderer()
+ setUpListeners()
+
+ // WHEN a shade list is built empty
+ onRenderListListener.onRenderList(listOf())
+
+ // VERIFY that the stack listener is invoked once but other listeners are not
+ verify(onAfterRenderListListener, times(1)).onAfterRenderList(any(), any())
+ verify(onAfterRenderGroupListener, never()).onAfterRenderGroup(any(), any())
+ verify(onAfterRenderEntryListener, never()).onAfterRenderEntry(any(), any())
+ verifyNoMoreInteractions(
+ onAfterRenderListListener,
+ onAfterRenderGroupListener,
+ onAfterRenderEntryListener
+ )
+ }
+
+ private fun listWith2Groups8Entries() = listOf(
+ group(
+ notif(1),
+ notif(2),
+ notif(3)
+ ),
+ notif(4),
+ group(
+ notif(5),
+ notif(6),
+ notif(7)
+ ),
+ notif(8)
+ )
+
+ private class FakeNotifViewRenderer : NotifViewRenderer {
+ override fun onRenderList(notifList: List<ListEntry>) {}
+ override fun getStackController(): NotifStackController = mock()
+ override fun getGroupController(group: GroupEntry): NotifGroupController = mock()
+ override fun getRowController(entry: NotificationEntry): NotifRowController = mock()
+ override fun onDispatchComplete() {}
+ }
+
+ private fun notif(id: Int): NotificationEntry = NotificationEntryBuilder().setId(id).build()
+
+ private fun group(summary: NotificationEntry, vararg children: NotificationEntry): GroupEntry =
+ GroupEntryBuilder().setSummary(summary).setChildren(children.toList()).build()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index fa25c3f..e9e1911 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -39,7 +39,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.Pair;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -49,6 +48,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
+import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import org.junit.Assert;
@@ -212,7 +212,7 @@
// public notification is custom layout - no header
mGroupRow.setSensitive(true, true);
mGroupRow.setOnFeedbackClickListener(null);
- mGroupRow.showFeedbackIcon(false, null);
+ mGroupRow.setFeedbackIcon(null);
}
@Test
@@ -226,13 +226,13 @@
mGroupRow.setChildrenContainer(mockContainer);
final boolean show = true;
- final Pair<Integer, Integer> resIds = new Pair(R.drawable.ic_feedback_alerted,
- R.string.notification_feedback_indicator_alerted);
- mGroupRow.showFeedbackIcon(show, resIds);
+ final FeedbackIcon icon = new FeedbackIcon(
+ R.drawable.ic_feedback_alerted, R.string.notification_feedback_indicator_alerted);
+ mGroupRow.setFeedbackIcon(icon);
- verify(mockContainer, times(1)).showFeedbackIcon(show, resIds);
- verify(privateLayout, times(1)).showFeedbackIcon(show, resIds);
- verify(publicLayout, times(1)).showFeedbackIcon(show, resIds);
+ verify(mockContainer, times(1)).setFeedbackIcon(icon);
+ verify(privateLayout, times(1)).setFeedbackIcon(icon);
+ verify(publicLayout, times(1)).setFeedbackIcon(icon);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 94e273b..682ff1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -23,7 +23,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.util.Pair;
import android.view.NotificationHeaderView;
import android.view.View;
import android.view.ViewPropertyAnimator;
@@ -36,6 +35,7 @@
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
+import com.android.systemui.statusbar.notification.FeedbackIcon;
import org.junit.Before;
import org.junit.Test;
@@ -76,7 +76,7 @@
@Test
@UiThreadTest
- public void testShowFeedbackIcon() {
+ public void testSetFeedbackIcon() {
View mockContracted = mock(NotificationHeaderView.class);
when(mockContracted.findViewById(com.android.internal.R.id.feedback))
.thenReturn(mockContracted);
@@ -94,7 +94,7 @@
mView.setExpandedChild(mockExpanded);
mView.setHeadsUpChild(mockHeadsUp);
- mView.showFeedbackIcon(true, new Pair(R.drawable.ic_feedback_alerted,
+ mView.setFeedbackIcon(new FeedbackIcon(R.drawable.ic_feedback_alerted,
R.string.notification_feedback_indicator_alerted));
verify(mockContracted, times(1)).setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 185d9cd..9be2837 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -30,7 +30,6 @@
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
@@ -296,14 +295,10 @@
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- when(row.canViewBeDismissed()).thenReturn(true);
- when(mStackScroller.getChildCount()).thenReturn(1);
- when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
mStackScroller.setIsRemoteInputActive(true);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
+ when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
.thenReturn(true);
- when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -312,14 +307,28 @@
}
@Test
+ public void testUpdateFooter_withoutNotifications() {
+ setBarStateForTest(StatusBarState.SHADE);
+ mStackScroller.setCurrentUserSetup(true);
+
+ when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
+ .thenReturn(false);
+
+ FooterView view = mock(FooterView.class);
+ mStackScroller.setFooterView(view);
+ mStackScroller.updateFooter();
+ verify(mStackScroller).updateFooterView(false, false, true);
+ }
+
+ @Test
public void testUpdateFooter_oneClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
- when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
+ when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
.thenReturn(true);
- when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -332,10 +341,9 @@
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(false);
- when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
+ when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
.thenReturn(true);
- when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -348,12 +356,8 @@
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- when(row.canViewBeDismissed()).thenReturn(false);
- when(mStackScroller.getChildCount()).thenReturn(1);
- when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
- when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
- when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
+ when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
.thenReturn(false);
when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 0faf5d4..a0e91fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -438,8 +438,8 @@
assertEquals("returns false when view is null", false,
NotificationSwipeHelper.isTouchInView(mEvent, null));
- doReturn(5f).when(mEvent).getX();
- doReturn(10f).when(mEvent).getY();
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mView).getWidth();
doReturn(20).when(mView).getHeight();
@@ -455,7 +455,7 @@
assertTrue("Touch is within the view",
mSwipeHelper.isTouchInView(mEvent, mView));
- doReturn(50f).when(mEvent).getX();
+ doReturn(50f).when(mEvent).getRawX();
assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mView));
@@ -466,8 +466,8 @@
assertEquals("returns false when view is null", false,
NotificationSwipeHelper.isTouchInView(mEvent, null));
- doReturn(5f).when(mEvent).getX();
- doReturn(10f).when(mEvent).getY();
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mNotificationRow).getWidth();
doReturn(20).when(mNotificationRow).getActualHeight();
@@ -483,7 +483,7 @@
assertTrue("Touch is within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
- doReturn(50f).when(mEvent).getX();
+ doReturn(50f).when(mEvent).getRawX();
assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 25fd801..07debe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -101,6 +102,8 @@
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
private BiometricUnlockController mBiometricUnlockController;
@Before
@@ -123,7 +126,7 @@
mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
mMetricsLogger, mDumpManager, mPowerManager,
mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
- mAuthController);
+ mAuthController, mStatusBarStateController);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
@@ -378,6 +381,23 @@
}
@Test
+ public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+ // GIVEN UDFPS is supported
+ when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+
+ // WHEN udfps fails twice - then don't show the bouncer
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+
+ // WHEN udfps fails the third time
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+
+ // THEN show the bouncer
+ verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ }
+
+ @Test
public void onFinishedGoingToSleep_authenticatesWhenPending() {
when(mUpdateMonitor.isGoingToSleep()).thenReturn(true);
mBiometricUnlockController.onFinishedGoingToSleep(-1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index bafbccd..db5fd26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -162,8 +162,11 @@
}
@Test
- public void testHeaderReadFromOldController() {
- mHeadsUpAppearanceController.setAppearFraction(1.0f, 1.0f);
+ public void constructor_animationValuesUpdated() {
+ float appearFraction = .75f;
+ float expandedHeight = 400f;
+ when(mStackScrollerController.getAppearFraction()).thenReturn(appearFraction);
+ when(mStackScrollerController.getExpandedHeight()).thenReturn(expandedHeight);
HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
@@ -179,14 +182,9 @@
new View(mContext),
new View(mContext),
new View(mContext));
- newController.readFrom(mHeadsUpAppearanceController);
- Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
- newController.mExpandedHeight, 0.0f);
- Assert.assertEquals(mHeadsUpAppearanceController.mAppearFraction,
- newController.mAppearFraction, 0.0f);
- Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
- newController.mIsExpanded);
+ Assert.assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f);
+ Assert.assertEquals(appearFraction, newController.mAppearFraction, 0.0f);
}
@Test
@@ -195,7 +193,9 @@
reset(mDarkIconDispatcher);
reset(mPanelView);
reset(mStackScrollerController);
- mHeadsUpAppearanceController.destroy();
+
+ mHeadsUpAppearanceController.onViewDetached();
+
verify(mHeadsUpManager).removeListener(any());
verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
verify(mPanelView).removeTrackingHeadsUpListener(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 210744e..3257a84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -47,8 +47,8 @@
@Test
fun initFrom_doesntCrash() {
- val other = LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
+ val other = LayoutInflater.from(mContext).inflate(R.layout.keyguard_bottom_area,
+ null, false) as KeyguardBottomAreaView
other.initFrom(mKeyguardBottomArea)
other.launchVoiceAssist()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 81ddc67..3ec9629 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -68,6 +68,7 @@
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
@@ -350,6 +351,8 @@
@Mock
private DumpManager mDumpManager;
@Mock
+ private InteractionJankMonitor mInteractionJankMonitor;
+ @Mock
private NotificationsQSContainerController mNotificationsQSContainerController;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
@@ -358,11 +361,13 @@
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners;
private FalsingManagerFake mFalsingManager = new FalsingManagerFake();
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager);
+ mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
+ mInteractionJankMonitor);
mKeyguardStatusView = new KeyguardStatusView(mContext);
mKeyguardStatusView.setId(R.id.keyguard_status_view);
@@ -425,7 +430,8 @@
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
mock(HeadsUpManagerPhone.class),
- new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager),
+ new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
+ mInteractionJankMonitor),
mKeyguardBypassController,
mDozeParameters,
mUnlockedScreenOffAnimationController);
@@ -464,8 +470,12 @@
.thenReturn(mUserSwitcherView);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
.thenReturn(mKeyguardBottomArea);
- when(mNotificationRemoteInputManager.isRemoteInputActive()).thenReturn(false);
-
+ when(mNotificationRemoteInputManager.isRemoteInputActive())
+ .thenReturn(false);
+ when(mInteractionJankMonitor.begin(any(), anyInt()))
+ .thenReturn(true);
+ when(mInteractionJankMonitor.end(anyInt()))
+ .thenReturn(true);
reset(mView);
mNotificationPanelViewController = new NotificationPanelViewController(mView,
@@ -511,7 +521,7 @@
mQuickAccessWalletController,
mQrCodeScannerController,
mRecordingController,
- new FakeExecutor(new FakeSystemClock()),
+ mExecutor,
mSecureSettings,
mSplitShadeHeaderController,
mUnlockedScreenOffAnimationController,
@@ -519,7 +529,8 @@
new PanelExpansionStateManager(),
mNotificationRemoteInputManager,
mSysUIUnfoldComponent,
- mControlsComponent);
+ mControlsComponent,
+ mInteractionJankMonitor);
mNotificationPanelViewController.initDependencies(
mStatusBar,
() -> {},
@@ -936,6 +947,7 @@
ArgumentCaptor.forClass(WeakReference.class);
monitorCallback.getValue().onSourceAvailable(new WeakReference<>(mCommunalSource));
+ mExecutor.runAllReady();
verify(mCommunalHostViewController).show(sourceCapture.capture());
assertThat(sourceCapture.getValue().get()).isEqualTo(mCommunalSource);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
index 0df7549..2d548e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -10,6 +10,7 @@
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -35,6 +36,8 @@
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var batteryMeterView: BatteryMeterView
@Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
+ @Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
+
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
@@ -56,8 +59,14 @@
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(false)
- splitShadeHeaderController = SplitShadeHeaderController(view, statusBarIconController,
- qsCarrierGroupControllerBuilder, featureFlags, batteryMeterViewController)
+ splitShadeHeaderController = SplitShadeHeaderController(
+ view,
+ statusBarIconController,
+ privacyIconsController,
+ qsCarrierGroupControllerBuilder,
+ featureFlags,
+ batteryMeterViewController
+ )
}
@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 6f174cb..c5bdfed 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
@@ -374,6 +374,21 @@
}
@Test
+ public void testHideAltAuth_onShowBouncer() {
+ // GIVEN alt auth is showing
+ mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ when(mBouncer.isShowing()).thenReturn(false);
+ when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
+ reset(mAlternateAuthInterceptor);
+
+ // WHEN showBouncer is called
+ mStatusBarKeyguardViewManager.showBouncer(true);
+
+ // THEN alt bouncer should be hidden
+ verify(mAlternateAuthInterceptor).hideAlternateAuthBouncer();
+ }
+
+ @Test
public void testUpdateResources_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateResources();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 348c181..07ec0e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -35,6 +35,7 @@
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Handler;
@@ -116,6 +117,8 @@
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
+ private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ @Mock
private Handler mHandler;
@Mock
private BubblesManager mBubblesManager;
@@ -137,7 +140,7 @@
@Mock
private OnUserInteractionCallback mOnUserInteractionCallback;
@Mock
- private NotificationActivityStarter mNotificationActivityStarter;
+ private StatusBarNotificationActivityStarter mNotificationActivityStarter;
@Mock
private ActivityLaunchAnimator mActivityLaunchAnimator;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -219,7 +222,7 @@
mock(NotificationLockscreenUserManager.class),
mShadeController,
mKeyguardStateController,
- mock(NotificationInterruptStateProvider.class),
+ mNotificationInterruptStateProvider,
mock(LockPatternUtils.class),
mock(StatusBarRemoteInputCallback.class),
mActivityIntentHelper,
@@ -375,4 +378,27 @@
// Notification should not be cancelled.
verify(mEntryManager, never()).performRemoveNotification(eq(sbn), any(), anyInt());
}
+
+ @Test
+ public void testOnFullScreenIntentWhenDozing_wakeUpDevice() {
+ // GIVEN entry that can has a full screen intent that can show
+ Notification.Builder nb = new Notification.Builder(mContext, "a")
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFullScreenIntent(mock(PendingIntent.class), true);
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0,
+ "tag" + System.currentTimeMillis(), 0, 0,
+ nb.build(), new UserHandle(0), null, 0);
+ NotificationEntry entry = mock(NotificationEntry.class);
+ when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(eq(entry)))
+ .thenReturn(true);
+
+ // WHEN
+ mNotificationActivityStarter.handleFullScreenIntent(entry);
+
+ // THEN display should try wake up for the full screen intent
+ verify(mStatusBar).wakeUpForFullScreenIntent();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 1df576e..a34d2f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -77,6 +77,7 @@
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -219,6 +220,7 @@
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
@Mock private NavigationBarController mNavigationBarController;
+ @Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
@Mock private BypassHeadsUpNotifier mBypassHeadsUpNotifier;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock private ColorExtractor.GradientColors mGradientColors;
@@ -279,7 +281,6 @@
@Mock private StartingSurface mStartingSurface;
@Mock private OperatorNameViewController mOperatorNameViewController;
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
- @Mock private PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
@Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock private NotifPipelineFlags mNotifPipelineFlags;
private ShadeController mShadeController;
@@ -417,6 +418,7 @@
mVisualStabilityManager,
mDeviceProvisionedController,
mNavigationBarController,
+ mAccessibilityFloatingMenuController,
() -> mAssistManager,
configurationController,
mNotificationShadeWindowController,
@@ -446,7 +448,6 @@
mExtensionController,
mUserInfoControllerImpl,
mOperatorNameViewControllerFactory,
- mPhoneStatusBarViewControllerFactory,
mPhoneStatusBarPolicy,
mKeyguardIndicationController,
mDemoModeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 609d69c..b97f053 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -21,7 +21,6 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
import android.app.Fragment;
@@ -48,9 +47,9 @@
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
@@ -62,12 +61,11 @@
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.Mockito;
-
-import java.util.Optional;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -82,16 +80,22 @@
// Set in instantiate()
private StatusBarIconController mStatusBarIconController;
private NetworkController mNetworkController;
- private StatusBarStateController mStatusBarStateController;
private KeyguardStateController mKeyguardStateController;
- private final StatusBar mStatusBar = mock(StatusBar.class);
private final CommandQueue mCommandQueue = mock(CommandQueue.class);
private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private OperatorNameViewController mOperatorNameViewController;
+ @Mock
private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
+ @Mock
private StatusBarFragmentComponent mStatusBarFragmentComponent;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ @Mock
+ private NotificationPanelViewController mNotificationPanelViewController;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -99,49 +103,35 @@
@Before
public void setup() {
- mStatusBarStateController = mDependency
- .injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- when(mStatusBar.getPanelController()).thenReturn(
- mock(NotificationPanelViewController.class));
}
@Test
- public void testDisableNone() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableNone() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
- .getVisibility());
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
- public void testDisableSystemInfo() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableSystemInfo() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
- assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
}
@Test
- public void testDisableNotifications() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableNotifications() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
@@ -153,25 +143,21 @@
}
@Test
- public void testDisableClock() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableClock() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
- assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
public void disable_noOngoingCall_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
@@ -183,9 +169,7 @@
@Test
public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -199,9 +183,7 @@
@Test
public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -214,9 +196,7 @@
@Test
public void disable_ongoingCallEnded_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -234,41 +214,95 @@
mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
}
- @Ignore("b/192618546")
@Test
- public void testOnDozingChanged() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
-
- fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
-
- Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
-
- reset(mStatusBarStateController);
+ public void disable_isDozingButNoCustomClock_clockAndSystemInfoVisible() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(false);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_customClockButNotDozing_clockAndSystemInfoVisible() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_dozingAndCustomClock_clockAndSystemInfoHidden() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+ // Make sure they start out as visible
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void onDozingChanged_clockAndSystemInfoVisibilitiesUpdated() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+ // Make sure they start out as visible
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+
fragment.onDozingChanged(true);
- Mockito.verify(mStatusBarStateController).isDozing();
- Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ // When this callback is triggered, we want to make sure the clock and system info
+ // visibilities are recalculated. Since dozing=true, they shouldn't be visible.
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
public void setUp_fragmentCreatesDaggerComponent() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent());
}
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
- mStatusBarFragmentComponentFactory =
- mock(StatusBarFragmentComponent.Factory.class);
- mStatusBarFragmentComponent = mock(StatusBarFragmentComponent.class);
- when(mStatusBarFragmentComponentFactory.create(any()))
- .thenReturn(mStatusBarFragmentComponent);
+ MockitoAnnotations.initMocks(this);
+ setUpDaggerComponent();
mOngoingCallController = mock(OngoingCallController.class);
mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
mLocationPublisher = mock(StatusBarLocationPublisher.class);
@@ -294,10 +328,9 @@
new StatusBarHideIconsForBouncerManager(
mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
mKeyguardStateController,
- mock(NotificationPanelViewController.class),
+ mNotificationPanelViewController,
mNetworkController,
mStatusBarStateController,
- () -> Optional.of(mStatusBar),
mCommandQueue,
new CollapsedStatusBarFragmentLogger(
new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)),
@@ -306,6 +339,13 @@
mOperatorNameViewControllerFactory);
}
+ private void setUpDaggerComponent() {
+ when(mStatusBarFragmentComponentFactory.create(any()))
+ .thenReturn(mStatusBarFragmentComponent);
+ when(mStatusBarFragmentComponent.getHeadsUpAppearanceController())
+ .thenReturn(mHeadsUpAppearanceController);
+ }
+
private void setUpNotificationIconAreaController() {
mMockNotificationAreaController = mock(NotificationIconAreaController.class);
@@ -324,4 +364,18 @@
when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
mNotificationAreaInner);
}
+
+ private CollapsedStatusBarFragment resumeAndGetFragment() {
+ mFragments.dispatchResume();
+ processAllMessages();
+ return (CollapsedStatusBarFragment) mFragment;
+ }
+
+ private View getClockView() {
+ return mFragment.getView().findViewById(R.id.clock);
+ }
+
+ private View getSystemIconAreaView() {
+ return mFragment.getView().findViewById(R.id.system_icon_area);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index de2012a..fa2a906 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -40,6 +40,7 @@
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
@@ -94,6 +95,7 @@
@Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
@Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
@Mock private lateinit var threadedRenderer: ThreadedRenderer
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private lateinit var testableLooper: TestableLooper
private lateinit var uiBgExecutor: FakeExecutor
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -124,7 +126,8 @@
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE,
mock(FingerprintManager::class.java))
- `when`(userManager.canAddMoreUsers()).thenReturn(true)
+ `when`(userManager.canAddMoreUsers(eq(UserManager.USER_TYPE_FULL_SECONDARY)))
+ .thenReturn(true)
`when`(notificationShadeWindowView.context).thenReturn(context)
userSwitcherController = UserSwitcherController(
@@ -147,7 +150,8 @@
uiBgExecutor,
interactionJankMonitor,
latencyTracker,
- dumpManager)
+ dumpManager,
+ dialogLaunchAnimator)
userSwitcherController.mPauseRefreshUsers = true
// Since userSwitcherController involves InteractionJankMonitor.
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 ae7afce..1e15d2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
@@ -79,6 +80,7 @@
@Mock ShellCommandHandler mShellCommandHandler;
@Mock SizeCompatUI mSizeCompatUI;
@Mock ShellExecutor mSysUiMainExecutor;
+ @Mock DragAndDrop mDragAndDrop;
@Before
public void setUp() {
@@ -87,6 +89,7 @@
mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
Optional.of(mShellCommandHandler), Optional.of(mSizeCompatUI),
+ Optional.of(mDragAndDrop),
mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor,
mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer,
mWakefulnessLifecycle, mSysUiMainExecutor);
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 67bb726..f1599e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -39,6 +39,7 @@
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -1101,8 +1102,12 @@
try {
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
- return magnificationProcessor
- .setScaleAndCenter(displayId, scale, centerX, centerY, animate, mId);
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setScale(scale)
+ .setCenterX(centerX)
+ .setCenterY(centerY).build();
+ return magnificationProcessor.setMagnificationConfig(displayId, config, animate,
+ mId);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 572cfdc..52a6dc1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2382,6 +2382,7 @@
somethingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState);
somethingChanged |= readTouchExplorationEnabledSettingLocked(userState);
somethingChanged |= readHighTextContrastEnabledSettingLocked(userState);
+ somethingChanged |= readAudioDescriptionEnabledSettingLocked(userState);
somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState);
@@ -2454,6 +2455,19 @@
return false;
}
+ private boolean readAudioDescriptionEnabledSettingLocked(AccessibilityUserState userState) {
+ final boolean audioDescriptionByDefaultEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, 0,
+ userState.mUserId) == 1;
+ if (audioDescriptionByDefaultEnabled
+ != userState.isAudioDescriptionByDefaultEnabledLocked()) {
+ userState.setAudioDescriptionByDefaultEnabledLocked(audioDescriptionByDefaultEnabled);
+ return true;
+ }
+ return false;
+ }
+
private void updateTouchExplorationLocked(AccessibilityUserState userState) {
boolean touchExplorationEnabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
boolean serviceHandlesDoubleTapEnabled = false;
@@ -3418,6 +3432,23 @@
}
}
+ /**
+ * Gets the status of the audio description preference.
+ * @return {@code true} if the audio description is enabled, {@code false} otherwise.
+ */
+ @Override
+ public boolean isAudioDescriptionByDefaultEnabled() {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".isAudioDescriptionByDefaultEnabled",
+ FLAGS_ACCESSIBILITY_MANAGER);
+ }
+ synchronized (mLock) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+
+ return userState.isAudioDescriptionByDefaultEnabledLocked();
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
@@ -3528,9 +3559,8 @@
mConnectionId = service.mId;
- mClient = AccessibilityInteractionClient.getInstance(/* initializeCache= */false,
- mContext);
- mClient.addConnection(mConnectionId, service);
+ mClient = AccessibilityInteractionClient.getInstance(mContext);
+ mClient.addConnection(mConnectionId, service, /*initializeCache=*/false);
//TODO: (multi-display) We need to support multiple displays.
DisplayManager displayManager = (DisplayManager)
@@ -3806,6 +3836,9 @@
private final Uri mHighTextContrastUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED);
+ private final Uri mAudioDescriptionByDefaultUri = Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT);
+
private final Uri mAccessibilitySoftKeyboardModeUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
@@ -3852,6 +3885,8 @@
contentResolver.registerContentObserver(
mHighTextContrastUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
+ mAudioDescriptionByDefaultUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mShowImeWithHardKeyboardUri, false, this, UserHandle.USER_ALL);
@@ -3905,6 +3940,10 @@
if (readHighTextContrastEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
+ } else if (mAudioDescriptionByDefaultUri.equals(uri)) {
+ if (readAudioDescriptionEnabledSettingLocked(userState)) {
+ onUserStateChangedLocked(userState);
+ }
} else if (mAccessibilitySoftKeyboardModeUri.equals(uri)
|| mShowImeWithHardKeyboardUri.equals(uri)) {
userState.reconcileSoftKeyboardModeWithSettingsLocked();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index e9f5870..bcb3413 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -406,7 +406,7 @@
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
synchronized (mLock) {
- if (mSecurityPolicy.canPerformGestures(this)) {
+ if (mServiceInterface != null && mSecurityPolicy.canPerformGestures(this)) {
MotionEventInjector motionEventInjector =
mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
if (wmTracingEnabled()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 8c3ca34..9324e3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -105,6 +105,7 @@
private String mTargetAssignedToAccessibilityButton;
private boolean mBindInstantServiceAllowed;
+ private boolean mIsAudioDescriptionByDefaultRequested;
private boolean mIsAutoclickEnabled;
private boolean mIsDisplayMagnificationEnabled;
private boolean mIsFilterKeyEventsEnabled;
@@ -411,6 +412,10 @@
if (mIsTextHighContrastEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
}
+ if (mIsAudioDescriptionByDefaultRequested) {
+ clientState |=
+ AccessibilityManager.STATE_FLAG_AUDIO_DESCRIPTION_BY_DEFAULT_ENABLED;
+ }
clientState |= traceClientState;
@@ -506,6 +511,8 @@
pw.append(", magnificationModes=").append(String.valueOf(mMagnificationModes));
pw.append(", magnificationCapabilities=")
.append(String.valueOf(mMagnificationCapabilities));
+ pw.append(", audioDescriptionByDefaultEnabled=")
+ .append(String.valueOf(mIsAudioDescriptionByDefaultRequested));
pw.append("}");
pw.println();
pw.append(" shortcut key:{");
@@ -824,6 +831,14 @@
mIsTextHighContrastEnabled = enabled;
}
+ public boolean isAudioDescriptionByDefaultEnabledLocked() {
+ return mIsAudioDescriptionByDefaultRequested;
+ }
+
+ public void setAudioDescriptionByDefaultEnabledLocked(boolean enabled) {
+ mIsAudioDescriptionByDefaultRequested = enabled;
+ }
+
public boolean isTouchExplorationEnabledLocked() {
return mIsTouchExplorationEnabled;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 946d22e4..86777a2 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -537,6 +537,8 @@
// touch, we figure out what to do. If were waiting
// we resent the delayed callback and wait again.
mSendHoverEnterAndMoveDelayed.cancel();
+ // clear any hover events that might have been queued and never sent.
+ mSendHoverEnterAndMoveDelayed.clear();
mSendHoverExitDelayed.cancel();
// If a touch exploration gesture is in progress send events for its end.
if (mState.isTouchExploring()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 6473bf5..42a81e1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -20,7 +20,9 @@
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.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
+import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -74,6 +76,7 @@
private final PointF mTempPoint = new PointF();
private final Object mLock;
private final Context mContext;
+ @GuardedBy("mLock")
private final SparseArray<DisableMagnificationCallback>
mMagnificationEndRunnableSparseArray = new SparseArray();
@@ -84,6 +87,8 @@
@GuardedBy("mLock")
private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ @GuardedBy("mLock")
+ private int mLastActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
// Track the active user to reset the magnification and get the associated user settings.
private @UserIdInt int mUserId = UserHandle.USER_SYSTEM;
@GuardedBy("mLock")
@@ -206,7 +211,7 @@
final float scale = mScaleProvider.getScale(displayId);
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
- scale, magnificationCenter);
+ scale, magnificationCenter, true);
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, animationEndCallback);
} else {
@@ -216,6 +221,77 @@
setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
}
+ /**
+ * Transitions to the targeting magnification config mode with current center of the
+ * magnification mode if it is available. It disables the current magnifier immediately then
+ * transitions to the targeting magnifier.
+ *
+ * @param displayId The logical display id
+ * @param config The targeting magnification config
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ */
+ public void transitionMagnificationConfigMode(int displayId, MagnificationConfig config,
+ boolean animate) {
+ synchronized (mLock) {
+ final int targetMode = config.getMode();
+ final PointF currentBoundsCenter = getCurrentMagnificationBoundsCenterLocked(displayId,
+ targetMode);
+ final PointF magnificationCenter = new PointF(config.getCenterX(), config.getCenterY());
+ if (currentBoundsCenter != null) {
+ final float centerX = Float.isNaN(config.getCenterX())
+ ? currentBoundsCenter.x
+ : config.getCenterX();
+ final float centerY = Float.isNaN(config.getCenterY())
+ ? currentBoundsCenter.y
+ : config.getCenterY();
+ magnificationCenter.set(centerX, centerY);
+ }
+
+ final DisableMagnificationCallback animationCallback =
+ getDisableMagnificationEndRunnableLocked(displayId);
+ if (animationCallback != null) {
+ Slog.w(TAG, "Discard previous animation request");
+ animationCallback.setExpiredAndRemoveFromListLocked();
+ }
+
+ final FullScreenMagnificationController screenMagnificationController =
+ getFullScreenMagnificationController();
+ final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+ final float scale = mScaleProvider.getScale(displayId);
+ if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ screenMagnificationController.reset(displayId, false);
+ windowMagnificationMgr.enableWindowMagnification(displayId,
+ scale, magnificationCenter.x, magnificationCenter.y,
+ animate ? STUB_ANIMATION_CALLBACK : null);
+ } else if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+ windowMagnificationMgr.disableWindowMagnification(displayId, false, null);
+ if (!screenMagnificationController.isRegistered(displayId)) {
+ screenMagnificationController.register(displayId);
+ }
+ screenMagnificationController.setScaleAndCenter(displayId, scale,
+ magnificationCenter.x, magnificationCenter.y, animate,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ }
+ }
+ }
+
+ /**
+ * Return {@code true} if disable magnification animation callback of the display is running.
+ *
+ * @param displayId The logical display id
+ */
+ public boolean hasDisableMagnificationCallback(int displayId) {
+ synchronized (mLock) {
+ final DisableMagnificationCallback animationCallback =
+ getDisableMagnificationEndRunnableLocked(displayId);
+ if (animationCallback != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void onRequestMagnificationSpec(int displayId, int serviceId) {
final WindowMagnificationManager windowMagnificationManager;
@@ -239,6 +315,7 @@
synchronized (mLock) {
mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ mLastActivatedMode = mActivatedMode;
}
logMagnificationModeWithImeOnIfNeeded();
disableFullScreenMagnificationIfNeeded(displayId);
@@ -264,7 +341,8 @@
// Internal request may be for transition, so we just need to check external request.
final boolean isMagnifyByExternalRequest =
fullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId) > 0;
- if (isMagnifyByExternalRequest) {
+ if (isMagnifyByExternalRequest || isActivated(displayId,
+ ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)) {
fullScreenMagnificationController.reset(displayId, false);
}
}
@@ -276,8 +354,10 @@
synchronized (mLock) {
mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ mLastActivatedMode = mActivatedMode;
}
logMagnificationModeWithImeOnIfNeeded();
+ disableWindowMagnificationIfNeeded(displayId);
} else {
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
SystemClock.uptimeMillis() - mFullScreenModeEnabledTime);
@@ -289,6 +369,14 @@
updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
}
+ private void disableWindowMagnificationIfNeeded(int displayId) {
+ final WindowMagnificationManager windowMagnificationManager =
+ getWindowMagnificationMgr();
+ if (isActivated(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
+ windowMagnificationManager.disableWindowMagnification(displayId, false);
+ }
+ }
+
@Override
public void onImeWindowVisibilityChanged(boolean shown) {
synchronized (mLock) {
@@ -298,6 +386,16 @@
}
/**
+ * Returns the last activated magnification mode. If there is no activated magnifier before, it
+ * returns fullscreen mode by default.
+ */
+ public int getLastActivatedMode() {
+ synchronized (mLock) {
+ return mLastActivatedMode;
+ }
+ }
+
+ /**
* Wrapper method of logging the magnification activated mode and its duration of the usage
* when the magnification is disabled.
*
@@ -336,6 +434,7 @@
synchronized (mLock) {
fullMagnificationController = mFullScreenMagnificationController;
windowMagnificationManager = mWindowMagnificationMgr;
+ mLastActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
}
mScaleProvider.onUserChanged(userId);
@@ -462,7 +561,15 @@
return mTempPoint;
}
- private boolean isActivated(int displayId, int mode) {
+ /**
+ * Return {@code true} if the specified magnification mode on the given display is activated
+ * or not.
+ *
+ * @param displayId The logical displayId.
+ * @param mode It's either ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN or
+ * ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW.
+ */
+ public boolean isActivated(int displayId, int mode) {
boolean isActivated = false;
if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
synchronized (mLock) {
@@ -495,15 +602,17 @@
private final int mCurrentMode;
private final float mCurrentScale;
private final PointF mCurrentCenter = new PointF();
+ private final boolean mAnimate;
- DisableMagnificationCallback(TransitionCallBack transitionCallBack,
- int displayId, int targetMode, float scale, PointF currentCenter) {
+ DisableMagnificationCallback(@Nullable TransitionCallBack transitionCallBack,
+ int displayId, int targetMode, float scale, PointF currentCenter, boolean animate) {
mTransitionCallBack = transitionCallBack;
mDisplayId = displayId;
mTargetMode = targetMode;
mCurrentMode = mTargetMode ^ ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
mCurrentScale = scale;
mCurrentCenter.set(currentCenter);
+ mAnimate = animate;
}
@Override
@@ -521,7 +630,9 @@
applyMagnificationModeLocked(mTargetMode);
}
updateMagnificationButton(mDisplayId, mTargetMode);
- mTransitionCallBack.onResult(mDisplayId, success);
+ if (mTransitionCallBack != null) {
+ mTransitionCallBack.onResult(mDisplayId, success);
+ }
}
}
@@ -546,7 +657,9 @@
setExpiredAndRemoveFromListLocked();
applyMagnificationModeLocked(mCurrentMode);
updateMagnificationButton(mDisplayId, mCurrentMode);
- mTransitionCallBack.onResult(mDisplayId, true);
+ if (mTransitionCallBack != null) {
+ mTransitionCallBack.onResult(mDisplayId, true);
+ }
}
}
@@ -557,9 +670,13 @@
private void applyMagnificationModeLocked(int mode) {
if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
- getFullScreenMagnificationController().setScaleAndCenter(mDisplayId,
- mCurrentScale, mCurrentCenter.x,
- mCurrentCenter.y, true,
+ final FullScreenMagnificationController fullScreenMagnificationController =
+ getFullScreenMagnificationController();
+ if (!fullScreenMagnificationController.isRegistered(mDisplayId)) {
+ fullScreenMagnificationController.register(mDisplayId);
+ }
+ fullScreenMagnificationController.setScaleAndCenter(mDisplayId, mCurrentScale,
+ mCurrentCenter.x, mCurrentCenter.y, mAnimate,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index efc6d51..dda1c4f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -16,6 +16,13 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.MagnificationConfig.DEFAULT_MODE;
+import static android.accessibilityservice.MagnificationConfig.FULLSCREEN_MODE;
+import static android.accessibilityservice.MagnificationConfig.WINDOW_MODE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
+import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.graphics.Region;
@@ -23,6 +30,22 @@
* Processor class for AccessibilityService connection to control magnification on the specified
* display. This wraps the function of magnification controller.
*
+ * <p>
+ * If the magnification config uses {@link DEFAULT_MODE}. This processor will control the current
+ * activated magnifier on the display. If there is no magnifier activated, it controls
+ * full-screen magnifier by default.
+ * </p>
+ *
+ * <p>
+ * If the magnification config uses {@link FULLSCREEN_MODE}. This processor will control
+ * full-screen magnifier on the display.
+ * </p>
+ *
+ * <p>
+ * If the magnification config uses {@link WINDOW_MODE}. This processor will control
+ * the activated window magnifier on the display.
+ * </p>
+ *
* @see MagnificationController
* @see FullScreenMagnificationController
*/
@@ -35,53 +58,185 @@
}
/**
- * {@link FullScreenMagnificationController#getScale(int)}
+ * Gets the magnification config of the display.
+ *
+ * @param displayId The logical display id
+ * @return the magnification config
+ */
+ public @NonNull MagnificationConfig getMagnificationConfig(int displayId) {
+ final int mode = getControllingMode(displayId);
+ MagnificationConfig.Builder builder = new MagnificationConfig.Builder();
+ if (mode == FULLSCREEN_MODE) {
+ final FullScreenMagnificationController fullScreenMagnificationController =
+ mController.getFullScreenMagnificationController();
+ builder.setMode(mode)
+ .setScale(fullScreenMagnificationController.getScale(displayId))
+ .setCenterX(fullScreenMagnificationController.getCenterX(displayId))
+ .setCenterY(fullScreenMagnificationController.getCenterY(displayId));
+ } else if (mode == WINDOW_MODE) {
+ final WindowMagnificationManager windowMagnificationManager =
+ mController.getWindowMagnificationMgr();
+ builder.setMode(mode)
+ .setScale(windowMagnificationManager.getScale(displayId))
+ .setCenterX(windowMagnificationManager.getCenterX(displayId))
+ .setCenterY(windowMagnificationManager.getCenterY(displayId));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets the magnification config of the display. If animation is disabled, the transition
+ * is immediate.
+ *
+ * @param displayId The logical display id
+ * @param config The magnification config
+ * @param animate {@code true} to animate from the current config or
+ * {@code false} to set the config immediately
+ * @param id The ID of the service requesting the change
+ * @return {@code true} if the magnification spec changed, {@code false} if the spec did not
+ * change
+ */
+ public boolean setMagnificationConfig(int displayId, @NonNull MagnificationConfig config,
+ boolean animate, int id) {
+ if (transitionModeIfNeeded(displayId, config, animate)) {
+ return true;
+ }
+
+ int configMode = config.getMode();
+ if (configMode == DEFAULT_MODE) {
+ configMode = getControllingMode(displayId);
+ }
+ if (configMode == FULLSCREEN_MODE) {
+ return setScaleAndCenterForFullScreenMagnification(displayId, config.getScale(),
+ config.getCenterX(), config.getCenterY(),
+ animate, id);
+ } else if (configMode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
+ config.getScale(), config.getCenterX(), config.getCenterY());
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if transition magnification mode needed. And it is no need to transition
+ * mode when the controlling mode is unchanged or the controlling magnifier is not activated.
+ */
+ private boolean transitionModeIfNeeded(int displayId, MagnificationConfig config,
+ boolean animate) {
+ int currentMode = getControllingMode(displayId);
+ if (currentMode == config.getMode()
+ || !mController.hasDisableMagnificationCallback(displayId)) {
+ return false;
+ }
+ mController.transitionMagnificationConfigMode(displayId, config, animate);
+ return true;
+ }
+
+ /**
+ * Returns the magnification scale. If an animation is in progress,
+ * this reflects the end state of the animation.
+ *
+ * @param displayId The logical display id.
+ * @return the scale
*/
public float getScale(int displayId) {
- return mController.getFullScreenMagnificationController().getScale(displayId);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ return mController.getFullScreenMagnificationController().getScale(displayId);
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().getScale(displayId);
+ }
+ return 0;
}
/**
- * {@link FullScreenMagnificationController#getCenterX(int)}
+ * Returns the magnification center in X coordinate of the controlling magnification mode.
+ * If the service can control magnification but fullscreen magnifier is not registered, it will
+ * register the magnifier for this call then unregister the magnifier finally to make the
+ * magnification center correct.
+ *
+ * @param displayId The logical display id
+ * @param canControlMagnification Whether the service can control magnification
+ * @return the X coordinate
*/
public float getCenterX(int displayId, boolean canControlMagnification) {
- boolean registeredJustForThisCall = registerMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterX(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterX(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
+ }
}
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().getCenterX(displayId);
}
+ return 0;
}
/**
- * {@link FullScreenMagnificationController#getCenterY(int)}
+ * Returns the magnification center in Y coordinate of the controlling magnification mode.
+ * If the service can control magnification but fullscreen magnifier is not registered, it will
+ * register the magnifier for this call then unregister the magnifier finally to make the
+ * magnification center correct.
+ *
+ * @param displayId The logical display id
+ * @param canControlMagnification Whether the service can control magnification
+ * @return the Y coordinate
*/
public float getCenterY(int displayId, boolean canControlMagnification) {
- boolean registeredJustForThisCall = registerMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterY(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterY(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
+ }
}
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().getCenterY(displayId);
}
+ return 0;
}
/**
- * {@link FullScreenMagnificationController#getMagnificationRegion(int, Region)}
+ * Return the magnification bounds of the current controlling magnification on the given
+ * display. If the magnifier is not enabled, it returns an empty region.
+ * If the service can control magnification but fullscreen magnifier is not registered, it will
+ * register the magnifier for this call then unregister the magnifier finally to make
+ * the magnification region correct.
+ *
+ * @param displayId The logical display id
+ * @param outRegion the region to populate
+ * @param canControlMagnification Whether the service can control magnification
+ * @return outRegion the magnification bounds of full-screen magnifier or the magnification
+ * source bounds of window magnifier
*/
public Region getMagnificationRegion(int displayId, @NonNull Region outRegion,
boolean canControlMagnification) {
- boolean registeredJustForThisCall = registerMagnificationIfNeeded(displayId,
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
+ } else if (mode == WINDOW_MODE) {
+ mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+ outRegion);
+ }
+ return outRegion;
+ }
+
+ private void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
+ boolean canControlMagnification) {
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
canControlMagnification);
try {
mController.getFullScreenMagnificationController().getMagnificationRegion(displayId,
outRegion);
- return outRegion;
} finally {
if (registeredJustForThisCall) {
unregister(displayId);
@@ -89,67 +244,105 @@
}
}
- /**
- * {@link FullScreenMagnificationController#setScaleAndCenter(int, float, float, float, boolean,
- * int)}
- */
- public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
+ private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
+ float centerX, float centerY,
boolean animate, int id) {
if (!isRegistered(displayId)) {
register(displayId);
}
- return mController.getFullScreenMagnificationController().setScaleAndCenter(displayId,
+ return mController.getFullScreenMagnificationController().setScaleAndCenter(
+ displayId,
scale,
centerX, centerY, animate, id);
}
/**
- * {@link FullScreenMagnificationController#reset(int, boolean)}
+ * Resets the magnification on the given display. The reset mode could be full-screen or
+ * window if it is activated.
+ *
+ * @param displayId The logical display id.
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
*/
public boolean reset(int displayId, boolean animate) {
- return mController.getFullScreenMagnificationController().reset(displayId, animate);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ return mController.getFullScreenMagnificationController().reset(displayId, animate);
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().reset(displayId);
+ }
+ return false;
}
/**
* {@link FullScreenMagnificationController#resetIfNeeded(int, boolean)}
*/
+ // TODO: support window magnification
public void resetAllIfNeeded(int connectionId) {
mController.getFullScreenMagnificationController().resetAllIfNeeded(connectionId);
}
/**
+ * {@link FullScreenMagnificationController#isMagnifying(int)}
+ * {@link WindowMagnificationManager#isWindowMagnifierEnabled(int)}
+ */
+ public boolean isMagnifying(int displayId) {
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ return mController.getFullScreenMagnificationController().isMagnifying(displayId);
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the current controlling magnification mode on the given display.
+ * If there is no magnifier activated, it fallbacks to the last activated mode.
+ * And the last activated mode is {@link FULLSCREEN_MODE} by default.
+ *
+ * @param displayId The logical display id
+ */
+ public int getControllingMode(int displayId) {
+ if (mController.isActivated(displayId,
+ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
+ return WINDOW_MODE;
+ } else if (mController.isActivated(displayId,
+ ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)) {
+ return FULLSCREEN_MODE;
+ } else {
+ return (mController.getLastActivatedMode() == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)
+ ? WINDOW_MODE
+ : FULLSCREEN_MODE;
+ }
+ }
+
+ private boolean registerDisplayMagnificationIfNeeded(int displayId,
+ boolean canControlMagnification) {
+ if (!isRegistered(displayId) && canControlMagnification) {
+ register(displayId);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isRegistered(int displayId) {
+ return mController.getFullScreenMagnificationController().isRegistered(displayId);
+ }
+
+ /**
* {@link FullScreenMagnificationController#register(int)}
*/
- public void register(int displayId) {
+ private void register(int displayId) {
mController.getFullScreenMagnificationController().register(displayId);
}
/**
* {@link FullScreenMagnificationController#unregister(int)} (int)}
*/
- public void unregister(int displayId) {
+ private void unregister(int displayId) {
mController.getFullScreenMagnificationController().unregister(displayId);
}
-
- /**
- * {@link FullScreenMagnificationController#isMagnifying(int)}
- */
- public boolean isMagnifying(int displayId) {
- return mController.getFullScreenMagnificationController().isMagnifying(displayId);
- }
-
- /**
- * {@link FullScreenMagnificationController#isRegistered(int)}
- */
- public boolean isRegistered(int displayId) {
- return mController.getFullScreenMagnificationController().isRegistered(displayId);
- }
-
- private boolean registerMagnificationIfNeeded(int displayId, boolean canControlMagnification) {
- if (!isRegistered(displayId) && canControlMagnification) {
- register(displayId);
- return true;
- }
- return false;
- }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index 5277425..25dcc2a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -59,15 +59,19 @@
}
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable MagnificationAnimationCallback callback) {
if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".enableWindowMagnification",
FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
- + ";centerY=" + centerY + ";callback=" + callback);
+ + ";centerY=" + centerY + ";magnificationFrameOffsetRatioX="
+ + magnificationFrameOffsetRatioX + ";magnificationFrameOffsetRatioY="
+ + magnificationFrameOffsetRatioY + ";callback=" + callback);
}
try {
mConnection.enableWindowMagnification(displayId, scale, centerX, centerY,
+ magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY,
transformToRemoteCallback(callback, mTrace));
} catch (RemoteException e) {
if (DBG) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 7d8f545..820be28 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -18,6 +18,7 @@
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static java.util.Arrays.asList;
@@ -78,6 +79,8 @@
final DetectingState mDetectingState;
@VisibleForTesting
final PanningScalingGestureState mObservePanningScalingState;
+ @VisibleForTesting
+ final ViewportDraggingState mViewportDraggingState;
@VisibleForTesting
State mCurrentState;
@@ -105,6 +108,7 @@
policyFlags));
mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate);
mDetectingState = new DetectingState(context, mDetectTripleTap);
+ mViewportDraggingState = new ViewportDraggingState();
mObservePanningScalingState = new PanningScalingGestureState(
new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true,
new PanningScalingHandler.MagnificationDelegate() {
@@ -158,7 +162,8 @@
public void handleShortcutTriggered() {
final Point screenSize = mTempPoint;
getScreenSize(mTempPoint);
- toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f);
+ toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f,
+ WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
}
private void getScreenSize(Point outSize) {
@@ -171,14 +176,17 @@
return Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
}
- private void enableWindowMagnifier(float centerX, float centerY) {
+ private void enableWindowMagnifier(float centerX, float centerY,
+ @WindowMagnificationManager.WindowPosition int windowPosition) {
if (DEBUG_ALL) {
- Slog.i(mLogTag, "enableWindowMagnifier :" + centerX + ", " + centerY);
+ Slog.i(mLogTag, "enableWindowMagnifier :"
+ + centerX + ", " + centerY + ", " + windowPosition);
}
final float scale = MathUtils.constrain(
mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE);
- mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY);
+ mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY,
+ windowPosition);
}
private void disableWindowMagnifier() {
@@ -188,11 +196,12 @@
mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
}
- private void toggleMagnification(float centerX, float centerY) {
+ private void toggleMagnification(float centerX, float centerY,
+ @WindowMagnificationManager.WindowPosition int windowPosition) {
if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
disableWindowMagnifier();
} else {
- enableWindowMagnifier(centerX, centerY);
+ enableWindowMagnifier(centerX, centerY, windowPosition);
}
}
@@ -200,7 +209,17 @@
if (DEBUG_DETECTING) {
Slog.i(mLogTag, "onTripleTap()");
}
- toggleMagnification(up.getX(), up.getY());
+ toggleMagnification(up.getX(), up.getY(),
+ WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ }
+
+ private void onTripleTapAndHold(MotionEvent up) {
+ if (DEBUG_DETECTING) {
+ Slog.i(mLogTag, "onTripleTapAndHold()");
+ }
+ enableWindowMagnifier(up.getX(), up.getY(),
+ WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
+ transitionTo(mViewportDraggingState);
}
void resetToDetectState() {
@@ -319,6 +338,65 @@
}
}
+
+ /**
+ * This class handles motion events when the event dispatcher has
+ * determined that the user is performing a single-finger drag of the
+ * magnification viewport.
+ *
+ * Leaving this state until receiving {@link MotionEvent#ACTION_UP}
+ * or {@link MotionEvent#ACTION_CANCEL}.
+ */
+ final class ViewportDraggingState implements State {
+
+ private float mLastX = Float.NaN;
+ private float mLastY = Float.NaN;
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case ACTION_MOVE: {
+ if (!Float.isNaN(mLastX) && !Float.isNaN(mLastY)) {
+ float offsetX = event.getX() - mLastX;
+ float offsetY = event.getY() - mLastY;
+ mWindowMagnificationMgr.moveWindowMagnification(mDisplayId, offsetX,
+ offsetY);
+ }
+ mLastX = event.getX();
+ mLastY = event.getY();
+ }
+ break;
+
+ case ACTION_UP:
+ case ACTION_CANCEL: {
+ mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+ transitionTo(mDetectingState);
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void clear() {
+ mLastX = Float.NaN;
+ mLastY = Float.NaN;
+ }
+
+ @Override
+ public void onExit() {
+ clear();
+ }
+
+ @Override
+ public String toString() {
+ return "ViewportDraggingState{"
+ + "mLastX=" + mLastX
+ + ",mLastY=" + mLastY
+ + '}';
+ }
+ }
+
/**
* This class handles motion events in a duration to determine if the user is going to
* manipulate the window magnifier or want to interact with current UI. The rule of leaving
@@ -405,6 +483,8 @@
transitionTo(mObservePanningScalingState);
} else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
onTripleTap(motionEvent);
+ } else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD) {
+ onTripleTapAndHold(motionEvent);
} else {
mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue,
lastDownEventTime);
@@ -439,6 +519,7 @@
return "WindowMagnificationGestureHandler{"
+ "mDetectingState=" + mDetectingState
+ ", mDelegatingState=" + mDelegatingState
+ + ", mViewportDraggingState=" + mViewportDraggingState
+ ", mMagnifiedInteractionState=" + mObservePanningScalingState
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index e1ec273..9162064 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -20,13 +20,16 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -43,6 +46,9 @@
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper}
* create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
@@ -57,6 +63,25 @@
private static final String TAG = "WindowMagnificationMgr";
+ /**
+ * Indicate that the magnification window is at the magnification center.
+ */
+ public static final int WINDOW_POSITION_AT_CENTER = 0;
+
+ /**
+ * Indicate that the magnification window is at the top-left side of the magnification
+ * center. The offset is equal to a half of MirrorSurfaceView. So, the bottom-right corner
+ * of the window is at the magnification center.
+ */
+ public static final int WINDOW_POSITION_AT_TOP_LEFT = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "WINDOW_POSITION_AT_" }, value = {
+ WINDOW_POSITION_AT_CENTER,
+ WINDOW_POSITION_AT_TOP_LEFT
+ })
+ public @interface WindowPosition {}
+
private final Object mLock = new Object();
private final Context mContext;
@VisibleForTesting
@@ -271,41 +296,88 @@
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
+ * @return {@code true} if the magnification is enabled successfully.
*/
- void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
- enableWindowMagnification(displayId, scale, centerX, centerY, STUB_ANIMATION_CALLBACK);
+ public boolean enableWindowMagnification(int displayId, float scale, float centerX,
+ float centerY) {
+ return enableWindowMagnification(displayId, scale, centerX, centerY,
+ STUB_ANIMATION_CALLBACK);
}
/**
- * Enables window magnification with specified center and scale on the specified display and
+ * Enables window magnification with specified center and scale on the given display and
* animating the transition.
*
* @param displayId The logical display id.
* @param scale The target scale, must be >= 1.
- * @param centerX The screen-relative X coordinate around which to center,
+ * @param centerX The screen-relative X coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
- * @param centerY The screen-relative Y coordinate around which to center,
+ * @param centerY The screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param animationCallback Called when the animation result is valid.
+ * @return {@code true} if the magnification is enabled successfully.
*/
- void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- @Nullable MagnificationAnimationCallback animationCallback) {
+ public boolean enableWindowMagnification(int displayId, float scale, float centerX,
+ float centerY, @Nullable MagnificationAnimationCallback animationCallback) {
+ return enableWindowMagnification(displayId, scale, centerX, centerY, animationCallback,
+ WINDOW_POSITION_AT_CENTER);
+ }
+
+ /**
+ * Enables window magnification with specified center and scale on the given display and
+ * animating the transition.
+ *
+ * @param displayId The logical display id.
+ * @param scale The target scale, must be >= 1.
+ * @param centerX The screen-relative X coordinate around which to center for magnification,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param centerY The screen-relative Y coordinate around which to center for magnification,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param windowPosition Indicate the offset between window position and (centerX, centerY).
+ * @return {@code true} if the magnification is enabled successfully.
+ */
+ public boolean enableWindowMagnification(int displayId, float scale, float centerX,
+ float centerY, @WindowPosition int windowPosition) {
+ return enableWindowMagnification(displayId, scale, centerX, centerY,
+ STUB_ANIMATION_CALLBACK, windowPosition);
+ }
+
+ /**
+ * Enables window magnification with specified center and scale on the given display and
+ * animating the transition.
+ *
+ * @param displayId The logical display id.
+ * @param scale The target scale, must be >= 1.
+ * @param centerX The screen-relative X coordinate around which to center for
+ * magnification, or {@link Float#NaN} to leave unchanged.
+ * @param centerY The screen-relative Y coordinate around which to center for
+ * magnification, or {@link Float#NaN} to leave unchanged.
+ * @param animationCallback Called when the animation result is valid.
+ * @param windowPosition Indicate the offset between window position and (centerX, centerY).
+ * @return {@code true} if the magnification is enabled successfully.
+ */
+ public boolean enableWindowMagnification(int displayId, float scale, float centerX,
+ float centerY, @Nullable MagnificationAnimationCallback animationCallback,
+ @WindowPosition int windowPosition) {
final boolean enabled;
+ boolean previousEnabled;
synchronized (mLock) {
if (mConnectionWrapper == null) {
- return;
+ return false;
}
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
magnifier = createWindowMagnifier(displayId);
}
+ previousEnabled = magnifier.mEnabled;
enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
- animationCallback);
+ animationCallback, windowPosition);
}
- if (enabled) {
+ if (enabled && !previousEnabled) {
mCallback.onWindowMagnificationActivationState(displayId, true);
}
+ return enabled;
}
/**
@@ -464,7 +536,7 @@
* @param displayId The logical display id
* @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled.
*/
- float getCenterX(int displayId) {
+ public float getCenterX(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -480,7 +552,7 @@
* @param displayId The logical display id
* @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled.
*/
- float getCenterY(int displayId) {
+ public float getCenterY(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -491,6 +563,42 @@
}
/**
+ * Populates magnified bounds on the screen. And the populated magnified bounds would be
+ * empty If window magnifier is not activated.
+ *
+ * @param displayId The logical display id.
+ * @param outRegion the region to populate
+ */
+ public void getMagnificationSourceBounds(int displayId, @NonNull Region outRegion) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ outRegion.setEmpty();
+ } else {
+ outRegion.set(magnifier.mSourceBounds);
+ }
+ }
+ }
+
+ /**
+ * Resets the magnification scale and center.
+ *
+ * @param displayId The logical display id.
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
+ */
+ public boolean reset(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return false;
+ }
+ magnifier.reset();
+ return true;
+ }
+ }
+
+ /**
* Creates the windowMagnifier based on the specified display and stores it.
*
* @param displayId logical display id.
@@ -618,6 +726,8 @@
// The magnified bounds on the screen.
private final Rect mSourceBounds = new Rect();
+ private PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
+
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
mDisplayId = displayId;
mWindowMagnificationManager = windowMagnificationManager;
@@ -625,13 +735,17 @@
@GuardedBy("mLock")
boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
- @Nullable MagnificationAnimationCallback animationCallback) {
- if (mEnabled) {
- return false;
+ @Nullable MagnificationAnimationCallback animationCallback,
+ @WindowPosition int windowPosition) {
+ // Handle defaults. The scale may be NAN when just updating magnification center.
+ if (Float.isNaN(scale)) {
+ scale = getScale();
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
+ setMagnificationFrameOffsetRatioByWindowPosition(windowPosition);
if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
- centerX, centerY, animationCallback)) {
+ centerX, centerY, mMagnificationFrameOffsetRatio.x,
+ mMagnificationFrameOffsetRatio.y, animationCallback)) {
mScale = normScale;
mEnabled = true;
@@ -640,6 +754,19 @@
return false;
}
+ void setMagnificationFrameOffsetRatioByWindowPosition(@WindowPosition int windowPosition) {
+ switch (windowPosition) {
+ case WINDOW_POSITION_AT_CENTER: {
+ mMagnificationFrameOffsetRatio.set(0f, 0f);
+ }
+ break;
+ case WINDOW_POSITION_AT_TOP_LEFT: {
+ mMagnificationFrameOffsetRatio.set(-1f, -1f);
+ }
+ break;
+ }
+ }
+
@GuardedBy("mLock")
boolean disableWindowMagnificationInternal(
@Nullable MagnificationAnimationCallback animationResultCallback) {
@@ -723,9 +850,15 @@
}
private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
- float centerY, MagnificationAnimationCallback animationCallback) {
- return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
- displayId, scale, centerX, centerY, animationCallback);
+ float centerY, float magnificationFrameOffsetRatioX,
+ float magnificationFrameOffsetRatioY,
+ MagnificationAnimationCallback animationCallback) {
+ synchronized (mLock) {
+ return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
+ displayId, scale, centerX, centerY,
+ magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY,
+ animationCallback);
+ }
}
private boolean setScaleInternal(int displayId, float scale) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 5e2449d..422749e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -210,7 +210,7 @@
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler(),
- Context.RECEIVER_NOT_EXPORTED);
+ Context.RECEIVER_EXPORTED);
mAugmentedAutofillResolver = new FrameworkResourcesServiceNameResolver(getContext(),
com.android.internal.R.string.config_defaultAugmentedAutofillService);
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index fd573d5..594140e 100644
--- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
@@ -40,8 +40,8 @@
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
import com.android.server.backup.transport.OnTransportRegisteredListener;
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportClientManager;
+import com.android.server.backup.transport.TransportConnection;
+import com.android.server.backup.transport.TransportConnectionManager;
import com.android.server.backup.transport.TransportConnectionListener;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
@@ -65,7 +65,7 @@
private final @UserIdInt int mUserId;
private final PackageManager mPackageManager;
private final Set<ComponentName> mTransportWhitelist;
- private final TransportClientManager mTransportClientManager;
+ private final TransportConnectionManager mTransportConnectionManager;
private final TransportStats mTransportStats;
private OnTransportRegisteredListener mOnTransportRegisteredListener = (c, n) -> {};
@@ -73,8 +73,8 @@
* Lock for registered transports and currently selected transport.
*
* <p><b>Warning:</b> No calls to {@link IBackupTransport} or calls that result in transport
- * code being executed such as {@link TransportClient#connect(String)}} and its variants should
- * be made with this lock held, risk of deadlock.
+ * code being executed such as {@link TransportConnection#connect(String)}} and its variants
+ * should be made with this lock held, risk of deadlock.
*/
private final Object mTransportLock = new Object();
@@ -94,7 +94,8 @@
mTransportWhitelist = Preconditions.checkNotNull(whitelist);
mCurrentTransportName = selectedTransport;
mTransportStats = new TransportStats();
- mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats);
+ mTransportConnectionManager = new TransportConnectionManager(mUserId, context,
+ mTransportStats);
}
@VisibleForTesting
@@ -103,13 +104,13 @@
Context context,
Set<ComponentName> whitelist,
String selectedTransport,
- TransportClientManager transportClientManager) {
+ TransportConnectionManager transportConnectionManager) {
mUserId = userId;
mPackageManager = context.getPackageManager();
mTransportWhitelist = Preconditions.checkNotNull(whitelist);
mCurrentTransportName = selectedTransport;
mTransportStats = new TransportStats();
- mTransportClientManager = transportClientManager;
+ mTransportConnectionManager = transportConnectionManager;
}
/* Sets a listener to be called whenever a transport is registered. */
@@ -307,7 +308,7 @@
* transportConsumer}.
*
* <p><b>Warning:</b> Do NOT make any calls to {@link IBackupTransport} or call any variants of
- * {@link TransportClient#connect(String)} here, otherwise you risk deadlock.
+ * {@link TransportConnection#connect(String)} here, otherwise you risk deadlock.
*/
public void forEachRegisteredTransport(Consumer<String> transportConsumer) {
synchronized (mTransportLock) {
@@ -407,17 +408,17 @@
}
/**
- * Returns a {@link TransportClient} for {@code transportName} or {@code null} if not
+ * Returns a {@link TransportConnection} for {@code transportName} or {@code null} if not
* registered.
*
* @param transportName The name of the transport.
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
* details.
- * @return A {@link TransportClient} or null if not registered.
+ * @return A {@link TransportConnection} or null if not registered.
*/
@Nullable
- public TransportClient getTransportClient(String transportName, String caller) {
+ public TransportConnection getTransportClient(String transportName, String caller) {
try {
return getTransportClientOrThrow(transportName, caller);
} catch (TransportNotRegisteredException e) {
@@ -427,38 +428,38 @@
}
/**
- * Returns a {@link TransportClient} for {@code transportName} or throws if not registered.
+ * Returns a {@link TransportConnection} for {@code transportName} or throws if not registered.
*
* @param transportName The name of the transport.
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
* details.
- * @return A {@link TransportClient}.
+ * @return A {@link TransportConnection}.
* @throws TransportNotRegisteredException if the transport is not registered.
*/
- public TransportClient getTransportClientOrThrow(String transportName, String caller)
+ public TransportConnection getTransportClientOrThrow(String transportName, String caller)
throws TransportNotRegisteredException {
synchronized (mTransportLock) {
ComponentName component = getRegisteredTransportComponentLocked(transportName);
if (component == null) {
throw new TransportNotRegisteredException(transportName);
}
- return mTransportClientManager.getTransportClient(component, caller);
+ return mTransportConnectionManager.getTransportClient(component, caller);
}
}
/**
- * Returns a {@link TransportClient} for the current transport or {@code null} if not
+ * Returns a {@link TransportConnection} for the current transport or {@code null} if not
* registered.
*
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
* details.
- * @return A {@link TransportClient} or null if not registered.
+ * @return A {@link TransportConnection} or null if not registered.
* @throws IllegalStateException if no transport is selected.
*/
@Nullable
- public TransportClient getCurrentTransportClient(String caller) {
+ public TransportConnection getCurrentTransportClient(String caller) {
if (mCurrentTransportName == null) {
throw new IllegalStateException("No transport selected");
}
@@ -468,16 +469,16 @@
}
/**
- * Returns a {@link TransportClient} for the current transport or throws if not registered.
+ * Returns a {@link TransportConnection} for the current transport or throws if not registered.
*
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
* details.
- * @return A {@link TransportClient}.
+ * @return A {@link TransportConnection}.
* @throws TransportNotRegisteredException if the transport is not registered.
* @throws IllegalStateException if no transport is selected.
*/
- public TransportClient getCurrentTransportClientOrThrow(String caller)
+ public TransportConnection getCurrentTransportClientOrThrow(String caller)
throws TransportNotRegisteredException {
if (mCurrentTransportName == null) {
throw new IllegalStateException("No transport selected");
@@ -488,15 +489,15 @@
}
/**
- * Disposes of the {@link TransportClient}.
+ * Disposes of the {@link TransportConnection}.
*
- * @param transportClient The {@link TransportClient} to be disposed of.
+ * @param transportConnection The {@link TransportConnection} to be disposed of.
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
* details.
*/
- public void disposeOfTransportClient(TransportClient transportClient, String caller) {
- mTransportClientManager.disposeOfTransportClient(transportClient, caller);
+ public void disposeOfTransportClient(TransportConnection transportConnection, String caller) {
+ mTransportConnectionManager.disposeOfTransportClient(transportConnection, caller);
}
/**
@@ -637,15 +638,16 @@
Bundle extras = new Bundle();
extras.putBoolean(BackupTransport.EXTRA_TRANSPORT_REGISTRATION, true);
- TransportClient transportClient =
- mTransportClientManager.getTransportClient(
+ TransportConnection transportConnection =
+ mTransportConnectionManager.getTransportClient(
transportComponent, extras, callerLogString);
final IBackupTransport transport;
try {
- transport = transportClient.connectOrThrow(callerLogString);
+ transport = transportConnection.connectOrThrow(callerLogString);
} catch (TransportNotAvailableException e) {
Slog.e(TAG, "Couldn't connect to transport " + transportString + " for registration");
- mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString);
+ mTransportConnectionManager.disposeOfTransportClient(transportConnection,
+ callerLogString);
return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
@@ -667,7 +669,7 @@
result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
- mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString);
+ mTransportConnectionManager.disposeOfTransportClient(transportConnection, callerLogString);
return result;
}
@@ -695,7 +697,7 @@
}
public void dumpTransportClients(PrintWriter pw) {
- mTransportClientManager.dump(pw);
+ mTransportConnectionManager.dump(pw);
}
public void dumpTransportStats(PrintWriter pw) {
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
new file mode 100644
index 0000000..a3f6eb6
--- /dev/null
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -0,0 +1,232 @@
+/*
+ * 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.server.backup.transport;
+
+import android.annotation.Nullable;
+import android.app.backup.RestoreDescription;
+import android.app.backup.RestoreSet;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import com.android.internal.backup.IBackupTransport;
+
+/**
+ * Client to {@link com.android.internal.backup.IBackupTransport}. Manages the call to the remote
+ * transport service and delivers the results.
+ */
+public class BackupTransportClient {
+ private final IBackupTransport mTransportBinder;
+
+ BackupTransportClient(IBackupTransport transportBinder) {
+ mTransportBinder = transportBinder;
+ }
+
+ /**
+ * See {@link IBackupTransport#name()}.
+ */
+ public String name() throws RemoteException {
+ return mTransportBinder.name();
+ }
+
+ /**
+ * See {@link IBackupTransport#configurationIntent()}
+ */
+ public Intent configurationIntent() throws RemoteException {
+ return mTransportBinder.configurationIntent();
+ }
+
+ /**
+ * See {@link IBackupTransport#currentDestinationString()}
+ */
+ public String currentDestinationString() throws RemoteException {
+ return mTransportBinder.currentDestinationString();
+ }
+
+ /**
+ * See {@link IBackupTransport#dataManagementIntent()}
+ */
+ public Intent dataManagementIntent() throws RemoteException {
+ return mTransportBinder.dataManagementIntent();
+ }
+
+ /**
+ * See {@link IBackupTransport#dataManagementIntentLabel()}
+ */
+ @Nullable
+ public CharSequence dataManagementIntentLabel() throws RemoteException {
+ return mTransportBinder.dataManagementIntentLabel();
+ }
+
+ /**
+ * See {@link IBackupTransport#transportDirName()}
+ */
+ public String transportDirName() throws RemoteException {
+ return mTransportBinder.transportDirName();
+ }
+
+ /**
+ * See {@link IBackupTransport#initializeDevice()}
+ */
+ public int initializeDevice() throws RemoteException {
+ return mTransportBinder.initializeDevice();
+ }
+
+ /**
+ * See {@link IBackupTransport#clearBackupData(PackageInfo)}
+ */
+ public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
+ return mTransportBinder.clearBackupData(packageInfo);
+ }
+
+ /**
+ * See {@link IBackupTransport#finishBackup()}
+ */
+ public int finishBackup() throws RemoteException {
+ return mTransportBinder.finishBackup();
+ }
+
+ /**
+ * See {@link IBackupTransport#requestBackupTime()}
+ */
+ public long requestBackupTime() throws RemoteException {
+ return mTransportBinder.requestBackupTime();
+ }
+
+ /**
+ * See {@link IBackupTransport#performBackup(PackageInfo, ParcelFileDescriptor, int)}
+ */
+ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
+ throws RemoteException {
+ return mTransportBinder.performBackup(packageInfo, inFd, flags);
+ }
+
+ /**
+ * See {@link IBackupTransport#getAvailableRestoreSets()}
+ */
+ public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
+ return mTransportBinder.getAvailableRestoreSets();
+ }
+
+ /**
+ * See {@link IBackupTransport#getCurrentRestoreSet()}
+ */
+ public long getCurrentRestoreSet() throws RemoteException {
+ return mTransportBinder.getCurrentRestoreSet();
+ }
+
+ /**
+ * See {@link IBackupTransport#startRestore(long, PackageInfo[])}
+ */
+ public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
+ return mTransportBinder.startRestore(token, packages);
+ }
+
+ /**
+ * See {@link IBackupTransport#nextRestorePackage()}
+ */
+ public RestoreDescription nextRestorePackage() throws RemoteException {
+ return mTransportBinder.nextRestorePackage();
+ }
+
+ /**
+ * See {@link IBackupTransport#getRestoreData(ParcelFileDescriptor)}
+ */
+ public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
+ return mTransportBinder.getRestoreData(outFd);
+ }
+
+ /**
+ * See {@link IBackupTransport#finishRestore()}
+ */
+ public void finishRestore() throws RemoteException {
+ mTransportBinder.finishRestore();
+ }
+
+ /**
+ * See {@link IBackupTransport#requestFullBackupTime()}
+ */
+ public long requestFullBackupTime() throws RemoteException {
+ return mTransportBinder.requestFullBackupTime();
+ }
+
+ /**
+ * See {@link IBackupTransport#performFullBackup(PackageInfo, ParcelFileDescriptor, int)}
+ */
+ public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
+ int flags) throws RemoteException {
+ return mTransportBinder.performFullBackup(targetPackage, socket, flags);
+ }
+
+ /**
+ * See {@link IBackupTransport#checkFullBackupSize(long)}
+ */
+ public int checkFullBackupSize(long size) throws RemoteException {
+ return mTransportBinder.checkFullBackupSize(size);
+ }
+
+ /**
+ * See {@link IBackupTransport#sendBackupData(int)}
+ */
+ public int sendBackupData(int numBytes) throws RemoteException {
+ return mTransportBinder.sendBackupData(numBytes);
+ }
+
+ /**
+ * See {@link IBackupTransport#cancelFullBackup()}
+ */
+ public void cancelFullBackup() throws RemoteException {
+ mTransportBinder.cancelFullBackup();
+ }
+
+ /**
+ * See {@link IBackupTransport#isAppEligibleForBackup(PackageInfo, boolean)}
+ */
+ public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
+ throws RemoteException {
+ return mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup);
+ }
+
+ /**
+ * See {@link IBackupTransport#getBackupQuota(String, boolean)}
+ */
+ public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException {
+ return mTransportBinder.getBackupQuota(packageName, isFullBackup);
+ }
+
+ /**
+ * See {@link IBackupTransport#getNextFullRestoreDataChunk(ParcelFileDescriptor)}
+ */
+ public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
+ return mTransportBinder.getNextFullRestoreDataChunk(socket);
+ }
+
+ /**
+ * See {@link IBackupTransport#abortFullRestore()}
+ */
+ public int abortFullRestore() throws RemoteException {
+ return mTransportBinder.abortFullRestore();
+ }
+
+ /**
+ * See {@link IBackupTransport#getTransportFlags()}
+ */
+ public int getTransportFlags() throws RemoteException {
+ return mTransportBinder.getTransportFlags();
+ }
+}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/DelegatingTransport.java b/services/backup/backuplib/java/com/android/server/backup/transport/DelegatingTransport.java
deleted file mode 100644
index ab87080..0000000
--- a/services/backup/backuplib/java/com/android/server/backup/transport/DelegatingTransport.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.transport;
-
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupTransport;
-import android.app.backup.RestoreDescription;
-import android.app.backup.RestoreSet;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import com.android.internal.backup.IBackupTransport;
-
-/**
- * Delegates all transport methods to the delegate() implemented in the derived class.
- */
-public abstract class DelegatingTransport extends IBackupTransport.Stub {
- protected abstract IBackupTransport getDelegate() throws RemoteException;
-
- /**
- * Ask the transport for the name under which it should be registered. This will
- * typically be its host service's component name, but need not be.
- */
- @Override
- public String name() throws RemoteException {
- return getDelegate().name();
- }
-
- /**
- * Ask the transport for an Intent that can be used to launch any internal
- * configuration Activity that it wishes to present. For example, the transport
- * may offer a UI for allowing the user to supply login credentials for the
- * transport's off-device backend.
- *
- * If the transport does not supply any user-facing configuration UI, it should
- * return null from this method.
- *
- * @return An Intent that can be passed to Context.startActivity() in order to
- * launch the transport's configuration UI. This method will return null
- * if the transport does not offer any user-facing configuration UI.
- */
- @Override
- public Intent configurationIntent() throws RemoteException {
- return getDelegate().configurationIntent();
- }
-
- /**
- * On demand, supply a one-line string that can be shown to the user that
- * describes the current backend destination. For example, a transport that
- * can potentially associate backup data with arbitrary user accounts should
- * include the name of the currently-active account here.
- *
- * @return A string describing the destination to which the transport is currently
- * sending data. This method should not return null.
- */
- @Override
- public String currentDestinationString() throws RemoteException {
- return getDelegate().currentDestinationString();
- }
-
- /**
- * Ask the transport for an Intent that can be used to launch a more detailed
- * secondary data management activity. For example, the configuration intent might
- * be one for allowing the user to select which account they wish to associate
- * their backups with, and the management intent might be one which presents a
- * UI for managing the data on the backend.
- *
- * <p>In the Settings UI, the configuration intent will typically be invoked
- * when the user taps on the preferences item labeled with the current
- * destination string, and the management intent will be placed in an overflow
- * menu labelled with the management label string.
- *
- * <p>If the transport does not supply any user-facing data management
- * UI, then it should return {@code null} from this method.
- *
- * @return An intent that can be passed to Context.startActivity() in order to
- * launch the transport's data-management UI. This method will return
- * {@code null} if the transport does not offer any user-facing data
- * management UI.
- */
- @Override
- public Intent dataManagementIntent() throws RemoteException {
- return getDelegate().dataManagementIntent();
- }
-
- /**
- * On demand, supply a short {@link CharSequence} that can be shown to the user as the
- * label on
- * an overflow menu item used to invoke the data management UI.
- *
- * @return A {@link CharSequence} to be used as the label for the transport's data management
- * affordance. If the transport supplies a data management intent, this
- * method must not return {@code null}.
- */
- @Override
- public CharSequence dataManagementIntentLabel() throws RemoteException {
- return getDelegate().dataManagementIntentLabel();
- }
-
- /**
- * Ask the transport where, on local device storage, to keep backup state blobs.
- * This is per-transport so that mock transports used for testing can coexist with
- * "live" backup services without interfering with the live bookkeeping. The
- * returned string should be a name that is expected to be unambiguous among all
- * available backup transports; the name of the class implementing the transport
- * is a good choice. This MUST be constant.
- *
- * @return A unique name, suitable for use as a file or directory name, that the
- * Backup Manager could use to disambiguate state files associated with
- * different backup transports.
- */
- @Override
- public String transportDirName() throws RemoteException {
- return getDelegate().transportDirName();
- }
-
- /**
- * Verify that this is a suitable time for a backup pass. This should return zero
- * if a backup is reasonable right now, some positive value otherwise. This method
- * will be called outside of the {@link #startSession}/{@link #endSession} pair.
- *
- * <p>If this is not a suitable time for a backup, the transport should return a
- * backoff delay, in milliseconds, after which the Backup Manager should try again.
- *
- * @return Zero if this is a suitable time for a backup pass, or a positive time delay
- * in milliseconds to suggest deferring the backup pass for a while.
- */
- @Override
- public long requestBackupTime() throws RemoteException {
- return getDelegate().requestBackupTime();
- }
-
- /**
- * Initialize the server side storage for this device, erasing all stored data.
- * The transport may send the request immediately, or may buffer it. After
- * this is called, {@link #finishBackup} must be called to ensure the request
- * is sent and received successfully.
- *
- * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or
- * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
- */
- @Override
- public int initializeDevice() throws RemoteException {
- return getDelegate().initializeDevice();
- }
-
- /**
- * Send one application's data to the backup destination. The transport may send
- * the data immediately, or may buffer it. After this is called, {@link #finishBackup}
- * must be called to ensure the data is sent and recorded successfully.
- *
- * @param packageInfo The identity of the application whose data is being backed up.
- * This specifically includes the signature list for the package.
- * @param inFd Descriptor of file with data that resulted from invoking the application's
- * BackupService.doBackup() method. This may be a pipe rather than a file on
- * persistent media, so it may not be seekable.
- * @param flags Some of {@link BackupTransport#FLAG_USER_INITIATED}.
- * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far),
- * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or
- * {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
- * become lost due to inactive expiry or some other reason and needs re-initializing)
- */
- @Override
- public int performBackup(PackageInfo packageInfo,
- ParcelFileDescriptor inFd, int flags) throws RemoteException {
- return getDelegate().performBackup(packageInfo, inFd, flags);
- }
-
- /**
- * Erase the give application's data from the backup destination. This clears
- * out the given package's data from the current backup set, making it as though
- * the app had never yet been backed up. After this is called, {@link finishBackup}
- * must be called to ensure that the operation is recorded successfully.
- *
- * @return the same error codes as {@link #performBackup}.
- * @param packageInfo
- */
- @Override
- public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
- return getDelegate().clearBackupData(packageInfo);
- }
-
- /**
- * Finish sending application data to the backup destination. This must be
- * called after {@link #performBackup} or {@link clearBackupData} to ensure that
- * all data is sent. Only when this method returns true can a backup be assumed
- * to have succeeded.
- *
- * @return the same error codes as {@link #performBackup}.
- */
- @Override
- public int finishBackup() throws RemoteException {
- return getDelegate().finishBackup();
- }
-
- /**
- * Get the set of all backups currently available over this transport.
- *
- * @return Descriptions of the set of restore images available for this device,
- * or null if an error occurred (the attempt should be rescheduled).
- **/
- @Override
- public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
- return getDelegate().getAvailableRestoreSets();
- }
-
- /**
- * Get the identifying token of the backup set currently being stored from
- * this device. This is used in the case of applications wishing to restore
- * their last-known-good data.
- *
- * @return A token that can be passed to {@link #startRestore}, or 0 if there
- * is no backup set available corresponding to the current device state.
- */
- @Override
- public long getCurrentRestoreSet() throws RemoteException {
- return getDelegate().getCurrentRestoreSet();
- }
-
- /**
- * Start restoring application data from backup. After calling this function,
- * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
- * to walk through the actual application data.
- *
- * @param token A backup token as returned by {@link #getAvailableRestoreSets}
- * or {@link #getCurrentRestoreSet}.
- * @param packages List of applications to restore (if data is available).
- * Application data will be restored in the order given.
- * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call
- * {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR}
- * (an error occurred, the restore should be aborted and rescheduled).
- */
- @Override
- public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
- return getDelegate().startRestore(token, packages);
- }
-
- /**
- * Get the package name of the next application with data in the backup store, plus
- * a description of the structure of the restored archive: either TYPE_KEY_VALUE for
- * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream.
- *
- * <p>If the package name in the returned RestoreDescription object is the singleton
- * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available
- * in the current restore session: all packages described in startRestore() have been
- * processed.
- *
- * <p>If this method returns {@code null}, it means that a transport-level error has
- * occurred and the entire restore operation should be abandoned.
- *
- * @return A RestoreDescription object containing the name of one of the packages
- * supplied to {@link #startRestore} plus an indicator of the data type of that
- * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
- * no more packages can be restored in this session; or {@code null} to indicate
- * a transport-level error.
- */
- @Override
- public RestoreDescription nextRestorePackage() throws RemoteException {
- return getDelegate().nextRestorePackage();
- }
-
- /**
- * Get the data for the application returned by {@link #nextRestorePackage}.
- *
- * @param outFd An open, writable file into which the backup data should be stored.
- * @return the same error codes as {@link #startRestore}.
- */
- @Override
- public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
- return getDelegate().getRestoreData(outFd);
- }
-
- /**
- * End a restore session (aborting any in-process data transfer as necessary),
- * freeing any resources and connections used during the restore process.
- */
- @Override
- public void finishRestore() throws RemoteException {
- getDelegate().finishRestore();
- }
-
- @Override
- public long requestFullBackupTime() throws RemoteException {
- return getDelegate().requestFullBackupTime();
- }
-
- @Override
- public int performFullBackup(PackageInfo targetPackage,
- ParcelFileDescriptor socket, int flags) throws RemoteException {
- return getDelegate().performFullBackup(targetPackage, socket, flags);
- }
-
- @Override
- public int checkFullBackupSize(long size) throws RemoteException {
- return getDelegate().checkFullBackupSize(size);
- }
-
- @Override
- public int sendBackupData(int numBytes) throws RemoteException {
- return getDelegate().sendBackupData(numBytes);
- }
-
- @Override
- public void cancelFullBackup() throws RemoteException {
- getDelegate().cancelFullBackup();
- }
-
- /**
- * Ask the transport whether this app is eligible for backup.
- *
- * @param targetPackage The identity of the application.
- * @param isFullBackup If set, transport should check if app is eligible for full data backup,
- * otherwise to check if eligible for key-value backup.
- * @return Whether this app is eligible for backup.
- */
- @Override
- public boolean isAppEligibleForBackup(PackageInfo targetPackage,
- boolean isFullBackup) throws RemoteException {
- return getDelegate().isAppEligibleForBackup(targetPackage, isFullBackup);
- }
-
- /**
- * Ask the transport about current quota for backup size of the package.
- *
- * @param packageName ID of package to provide the quota.
- * @param isFullBackup If set, transport should return limit for full data backup, otherwise
- * for key-value backup.
- * @return Current limit on full data backup size in bytes.
- */
- @Override
- public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException {
- return getDelegate().getBackupQuota(packageName, isFullBackup);
- }
-
- /**
- * Ask the transport to provide data for the "current" package being restored. This
- * is the package that was just reported by {@link #nextRestorePackage()} as having
- * {@link RestoreDescription#TYPE_FULL_STREAM} data.
- *
- * The transport writes some data to the socket supplied to this call, and returns
- * the number of bytes written. The system will then read that many bytes and
- * stream them to the application's agent for restore, then will call this method again
- * to receive the next chunk of the archive. This sequence will be repeated until the
- * transport returns zero indicating that all of the package's data has been delivered
- * (or returns a negative value indicating some sort of hard error condition at the
- * transport level).
- *
- * <p>After this method returns zero, the system will then call
- * {@link #getNextFullRestorePackage()} to begin the restore process for the next
- * application, and the sequence begins again.
- *
- * <p>The transport should always close this socket when returning from this method.
- * Do not cache this socket across multiple calls or you may leak file descriptors.
- *
- * @param socket The file descriptor that the transport will use for delivering the
- * streamed archive. The transport must close this socket in all cases when returning
- * from this method.
- * @return 0 when no more data for the current package is available. A positive value
- * indicates the presence of that many bytes to be delivered to the app. Any negative
- * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
- * indicating a fatal error condition that precludes further restore operations
- * on the current dataset.
- */
- @Override
- public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
- return getDelegate().getNextFullRestoreDataChunk(socket);
- }
-
- /**
- * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
- * data for restore, it will invoke this method to tell the transport that it should
- * abandon the data download for the current package. The OS will then either call
- * {@link #nextRestorePackage()} again to move on to restoring the next package in the
- * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
- * operation.
- *
- * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
- * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
- * transport-level failure. If the transport reports an error here, the entire restore
- * operation will immediately be finished with no further attempts to restore app data.
- */
- @Override
- public int abortFullRestore() throws RemoteException {
- return getDelegate().abortFullRestore();
- }
-
- /**
- * Returns flags with additional information about the transport, which is accessible to the
- * {@link BackupAgent}. This allows the agent to decide what to backup or
- * restore based on properties of the transport.
- *
- * <p>For supported flags see {@link BackupAgent}.
- */
- @Override
- public int getTransportFlags() throws RemoteException {
- return getDelegate().getTransportFlags();
- }
-}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java
deleted file mode 100644
index 72b1ee7..0000000
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java
+++ /dev/null
@@ -1,206 +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.backup.transport;
-
-import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
-import static com.android.server.backup.transport.TransportUtils.formatMessage;
-
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.TransportManager;
-import com.android.server.backup.transport.TransportUtils.Priority;
-
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.WeakHashMap;
-import java.util.function.Function;
-
-/**
- * Manages the creation and disposal of {@link TransportClient}s. The only class that should use
- * this is {@link TransportManager}, all the other usages should go to {@link TransportManager}.
- */
-public class TransportClientManager {
- private static final String TAG = "TransportClientManager";
- private static final String SERVICE_ACTION_ENCRYPTING_TRANSPORT =
- "android.encryption.BACKUP_ENCRYPTION";
- private static final ComponentName ENCRYPTING_TRANSPORT = new ComponentName(
- "com.android.server.backup.encryption",
- "com.android.server.backup.encryption.BackupEncryptionService");
- private static final String ENCRYPTING_TRANSPORT_REAL_TRANSPORT_KEY = "transport";
-
- private final @UserIdInt int mUserId;
- private final Context mContext;
- private final TransportStats mTransportStats;
- private final Object mTransportClientsLock = new Object();
- private int mTransportClientsCreated = 0;
- private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
- private final Function<ComponentName, Intent> mIntentFunction;
-
- /**
- * Return an {@link Intent} which resolves to an intermediate {@link IBackupTransport} that
- * encrypts (or decrypts) the data when sending it (or receiving it) from the {@link
- * IBackupTransport} for the given {@link ComponentName}.
- */
- public static Intent getEncryptingTransportIntent(ComponentName tranportComponent) {
- return new Intent(SERVICE_ACTION_ENCRYPTING_TRANSPORT)
- .setComponent(ENCRYPTING_TRANSPORT)
- .putExtra(ENCRYPTING_TRANSPORT_REAL_TRANSPORT_KEY, tranportComponent);
- }
-
- /**
- * Return an {@link Intent} which resolves to the {@link IBackupTransport} for the {@link
- * ComponentName}.
- */
- private static Intent getRealTransportIntent(ComponentName transportComponent) {
- return new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
- }
-
- /**
- * Given a {@link Intent} originally created by {@link
- * #getEncryptingTransportIntent(ComponentName)}, returns the {@link Intent} which resolves to
- * the {@link IBackupTransport} for that {@link ComponentName}.
- */
- public static Intent getRealTransportIntent(Intent encryptingTransportIntent) {
- ComponentName transportComponent = encryptingTransportIntent.getParcelableExtra(
- ENCRYPTING_TRANSPORT_REAL_TRANSPORT_KEY);
- Intent intent = getRealTransportIntent(transportComponent)
- .putExtras(encryptingTransportIntent.getExtras());
- intent.removeExtra(ENCRYPTING_TRANSPORT_REAL_TRANSPORT_KEY);
- return intent;
- }
-
- /**
- * Create a {@link TransportClientManager} such that {@link #getTransportClient(ComponentName,
- * Bundle, String)} returns a {@link TransportClient} which connects to an intermediate {@link
- * IBackupTransport} that encrypts (or decrypts) the data when sending it (or receiving it) from
- * the {@link IBackupTransport} for the given {@link ComponentName}.
- */
- public static TransportClientManager createEncryptingClientManager(@UserIdInt int userId,
- Context context, TransportStats transportStats) {
- return new TransportClientManager(userId, context, transportStats,
- TransportClientManager::getEncryptingTransportIntent);
- }
-
- public TransportClientManager(@UserIdInt int userId, Context context,
- TransportStats transportStats) {
- this(userId, context, transportStats, TransportClientManager::getRealTransportIntent);
- }
-
- private TransportClientManager(@UserIdInt int userId, Context context,
- TransportStats transportStats, Function<ComponentName, Intent> intentFunction) {
- mUserId = userId;
- mContext = context;
- mTransportStats = transportStats;
- mIntentFunction = intentFunction;
- }
-
- /**
- * Retrieves a {@link TransportClient} for the transport identified by {@param
- * transportComponent}.
- *
- * @param transportComponent The {@link ComponentName} of the transport.
- * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
- * details.
- * @return A {@link TransportClient}.
- */
- public TransportClient getTransportClient(ComponentName transportComponent, String caller) {
- return getTransportClient(transportComponent, null, caller);
- }
-
- /**
- * Retrieves a {@link TransportClient} for the transport identified by {@param
- * transportComponent} whose binding intent will have the {@param extras} extras.
- *
- * @param transportComponent The {@link ComponentName} of the transport.
- * @param extras A {@link Bundle} of extras to pass to the binding intent.
- * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
- * details.
- * @return A {@link TransportClient}.
- */
- public TransportClient getTransportClient(
- ComponentName transportComponent, @Nullable Bundle extras, String caller) {
- Intent bindIntent = mIntentFunction.apply(transportComponent);
- if (extras != null) {
- bindIntent.putExtras(extras);
- }
- return getTransportClient(transportComponent, caller, bindIntent);
- }
-
- private TransportClient getTransportClient(
- ComponentName transportComponent, String caller, Intent bindIntent) {
- synchronized (mTransportClientsLock) {
- TransportClient transportClient =
- new TransportClient(
- mUserId,
- mContext,
- mTransportStats,
- bindIntent,
- transportComponent,
- Integer.toString(mTransportClientsCreated),
- caller);
- mTransportClientsCallerMap.put(transportClient, caller);
- mTransportClientsCreated++;
- TransportUtils.log(
- Priority.DEBUG,
- TAG,
- formatMessage(null, caller, "Retrieving " + transportClient));
- return transportClient;
- }
- }
-
- /**
- * Disposes of the {@link TransportClient}.
- *
- * @param transportClient The {@link TransportClient} to be disposed of.
- * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
- * details.
- */
- public void disposeOfTransportClient(TransportClient transportClient, String caller) {
- transportClient.unbind(caller);
- transportClient.markAsDisposed();
- synchronized (mTransportClientsLock) {
- TransportUtils.log(
- Priority.DEBUG,
- TAG,
- formatMessage(null, caller, "Disposing of " + transportClient));
- mTransportClientsCallerMap.remove(transportClient);
- }
- }
-
- public void dump(PrintWriter pw) {
- pw.println("Transport clients created: " + mTransportClientsCreated);
- synchronized (mTransportClientsLock) {
- pw.println("Current transport clients: " + mTransportClientsCallerMap.size());
- for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) {
- String caller = mTransportClientsCallerMap.get(transportClient);
- pw.println(" " + transportClient + " [" + caller + "]");
- for (String logEntry : transportClient.getLogBuffer()) {
- pw.println(" " + logEntry);
- }
- }
- }
- }
-}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
similarity index 94%
rename from services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
rename to services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
index 0eb3ea3..da77eba 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
@@ -59,16 +59,17 @@
import java.util.concurrent.ExecutionException;
/**
- * A {@link TransportClient} manages the connection to an {@link IBackupTransport} service, obtained
- * via the {@param bindIntent} parameter provided in the constructor. A {@link TransportClient} is
- * responsible for only one connection to the transport service, not more.
+ * A {@link TransportConnection} manages the connection to an {@link IBackupTransport} service,
+ * obtained via the {@param bindIntent} parameter provided in the constructor. A
+ * {@link TransportConnection} is responsible for only one connection to the transport service,
+ * not more.
*
* <p>After retrieved using {@link TransportManager#getTransportClient(String, String)}, you can
* call either {@link #connect(String)}, if you can block your thread, or {@link
* #connectAsync(TransportConnectionListener, String)}, otherwise, to obtain a {@link
* IBackupTransport} instance. It's meant to be passed around as a token to a connected transport.
* When the connection is not needed anymore you should call {@link #unbind(String)} or indirectly
- * via {@link TransportManager#disposeOfTransportClient(TransportClient, String)}.
+ * via {@link TransportManager#disposeOfTransportClient(TransportConnection, String)}.
*
* <p>DO NOT forget to unbind otherwise there will be dangling connections floating around.
*
@@ -76,8 +77,8 @@
*
* @see TransportManager
*/
-public class TransportClient {
- @VisibleForTesting static final String TAG = "TransportClient";
+public class TransportConnection {
+ @VisibleForTesting static final String TAG = "TransportConnection";
private static final int LOG_BUFFER_SIZE = 5;
private final @UserIdInt int mUserId;
@@ -107,7 +108,7 @@
@GuardedBy("mStateLock")
private volatile IBackupTransport mTransport;
- TransportClient(
+ TransportConnection(
@UserIdInt int userId,
Context context,
TransportStats transportStats,
@@ -127,7 +128,7 @@
}
@VisibleForTesting
- TransportClient(
+ TransportConnection(
@UserIdInt int userId,
Context context,
TransportStats transportStats,
@@ -144,7 +145,7 @@
mIdentifier = identifier;
mCreatorLogString = caller;
mListenerHandler = listenerHandler;
- mConnection = new TransportConnection(context, this);
+ mConnection = new TransportConnectionMonitor(context, this);
// For logging
String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", "");
@@ -192,7 +193,7 @@
* For unusable transport binders check {@link DeadObjectException}.
*
* @param listener The listener that will be called with the (possibly null or unusable) {@link
- * IBackupTransport} instance and this {@link TransportClient} object.
+ * IBackupTransport} instance and this {@link TransportConnection} object.
* @param caller A {@link String} identifying the caller for logging/debugging purposes. This
* should be a human-readable short string that is easily identifiable in the logs. Ideally
* TAG.methodName(), where TAG is the one used in logcat. In cases where this is is not very
@@ -373,8 +374,8 @@
}
/**
- * If the {@link TransportClient} is already connected to the transport, returns the transport,
- * otherwise throws {@link TransportNotAvailableException}.
+ * If the {@link TransportConnection} is already connected to the transport, returns the
+ * transport, otherwise throws {@link TransportNotAvailableException}.
*
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
* {@link #connectAsync(TransportConnectionListener, String)} for more details.
@@ -647,19 +648,20 @@
* This class is a proxy to TransportClient methods that doesn't hold a strong reference to the
* TransportClient, allowing it to be GC'ed. If the reference was lost it logs a message.
*/
- private static class TransportConnection implements ServiceConnection {
+ private static class TransportConnectionMonitor implements ServiceConnection {
private final Context mContext;
- private final WeakReference<TransportClient> mTransportClientRef;
+ private final WeakReference<TransportConnection> mTransportClientRef;
- private TransportConnection(Context context, TransportClient transportClient) {
+ private TransportConnectionMonitor(Context context,
+ TransportConnection transportConnection) {
mContext = context;
- mTransportClientRef = new WeakReference<>(transportClient);
+ mTransportClientRef = new WeakReference<>(transportConnection);
}
@Override
public void onServiceConnected(ComponentName transportComponent, IBinder binder) {
- TransportClient transportClient = mTransportClientRef.get();
- if (transportClient == null) {
+ TransportConnection transportConnection = mTransportClientRef.get();
+ if (transportConnection == null) {
referenceLost("TransportConnection.onServiceConnected()");
return;
}
@@ -667,30 +669,30 @@
// In short-term, blocking calls are OK as the transports come from the allowlist at
// {@link SystemConfig#getBackupTransportWhitelist()}
Binder.allowBlocking(binder);
- transportClient.onServiceConnected(binder);
+ transportConnection.onServiceConnected(binder);
}
@Override
public void onServiceDisconnected(ComponentName transportComponent) {
- TransportClient transportClient = mTransportClientRef.get();
- if (transportClient == null) {
+ TransportConnection transportConnection = mTransportClientRef.get();
+ if (transportConnection == null) {
referenceLost("TransportConnection.onServiceDisconnected()");
return;
}
- transportClient.onServiceDisconnected();
+ transportConnection.onServiceDisconnected();
}
@Override
public void onBindingDied(ComponentName transportComponent) {
- TransportClient transportClient = mTransportClientRef.get();
- if (transportClient == null) {
+ TransportConnection transportConnection = mTransportClientRef.get();
+ if (transportConnection == null) {
referenceLost("TransportConnection.onBindingDied()");
return;
}
- transportClient.onBindingDied();
+ transportConnection.onBindingDied();
}
- /** @see TransportClient#finalize() */
+ /** @see TransportConnection#finalize() */
private void referenceLost(String caller) {
mContext.unbindService(this);
TransportUtils.log(
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
index 1ccffd0..03d35e4 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
@@ -21,17 +21,18 @@
import com.android.internal.backup.IBackupTransport;
/**
- * Listener to be called by {@link TransportClient#connectAsync(TransportConnectionListener,
+ * Listener to be called by {@link TransportConnection#connectAsync(TransportConnectionListener,
* String)}.
*/
public interface TransportConnectionListener {
/**
- * Called when {@link TransportClient} has a transport binder available or that it decided it
- * couldn't obtain one, in which case {@param transport} is null.
+ * Called when {@link TransportConnection} has a transport binder available or that it decided
+ * it couldn't obtain one, in which case {@param transport} is null.
*
* @param transport A {@link IBackupTransport} transport binder or null.
- * @param transportClient The {@link TransportClient} used to retrieve this transport binder.
+ * @param transportConnection The {@link TransportConnection} used to retrieve this transport
+ * binder.
*/
void onTransportConnectionResult(
- @Nullable IBackupTransport transport, TransportClient transportClient);
+ @Nullable IBackupTransport transport, TransportConnection transportConnection);
}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionManager.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionManager.java
new file mode 100644
index 0000000..16acb18
--- /dev/null
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionManager.java
@@ -0,0 +1,163 @@
+/*
+ * 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.backup.transport;
+
+import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.transport.TransportUtils.Priority;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.function.Function;
+
+/**
+ * Manages the creation and disposal of {@link TransportConnection}s. The only class that should use
+ * this is {@link TransportManager}, all the other usages should go to {@link TransportManager}.
+ */
+public class TransportConnectionManager {
+ private static final String TAG = "TransportConnectionManager";
+
+ private final @UserIdInt int mUserId;
+ private final Context mContext;
+ private final TransportStats mTransportStats;
+ private final Object mTransportClientsLock = new Object();
+ private int mTransportClientsCreated = 0;
+ private Map<TransportConnection, String> mTransportClientsCallerMap = new WeakHashMap<>();
+ private final Function<ComponentName, Intent> mIntentFunction;
+
+ /**
+ * Return an {@link Intent} which resolves to the {@link IBackupTransport} for the {@link
+ * ComponentName}.
+ */
+ private static Intent getRealTransportIntent(ComponentName transportComponent) {
+ return new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
+ }
+
+ public TransportConnectionManager(@UserIdInt int userId, Context context,
+ TransportStats transportStats) {
+ this(userId, context, transportStats, TransportConnectionManager::getRealTransportIntent);
+ }
+
+ private TransportConnectionManager(@UserIdInt int userId, Context context,
+ TransportStats transportStats, Function<ComponentName, Intent> intentFunction) {
+ mUserId = userId;
+ mContext = context;
+ mTransportStats = transportStats;
+ mIntentFunction = intentFunction;
+ }
+
+ /**
+ * Retrieves a {@link TransportConnection} for the transport identified by {@param
+ * transportComponent}.
+ *
+ * @param transportComponent The {@link ComponentName} of the transport.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportConnection}.
+ */
+ public TransportConnection getTransportClient(ComponentName transportComponent, String caller) {
+ return getTransportClient(transportComponent, null, caller);
+ }
+
+ /**
+ * Retrieves a {@link TransportConnection} for the transport identified by {@param
+ * transportComponent} whose binding intent will have the {@param extras} extras.
+ *
+ * @param transportComponent The {@link ComponentName} of the transport.
+ * @param extras A {@link Bundle} of extras to pass to the binding intent.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportConnection}.
+ */
+ public TransportConnection getTransportClient(
+ ComponentName transportComponent, @Nullable Bundle extras, String caller) {
+ Intent bindIntent = mIntentFunction.apply(transportComponent);
+ if (extras != null) {
+ bindIntent.putExtras(extras);
+ }
+ return getTransportClient(transportComponent, caller, bindIntent);
+ }
+
+ private TransportConnection getTransportClient(
+ ComponentName transportComponent, String caller, Intent bindIntent) {
+ synchronized (mTransportClientsLock) {
+ TransportConnection transportConnection =
+ new TransportConnection(
+ mUserId,
+ mContext,
+ mTransportStats,
+ bindIntent,
+ transportComponent,
+ Integer.toString(mTransportClientsCreated),
+ caller);
+ mTransportClientsCallerMap.put(transportConnection, caller);
+ mTransportClientsCreated++;
+ TransportUtils.log(
+ Priority.DEBUG,
+ TAG,
+ formatMessage(null, caller, "Retrieving " + transportConnection));
+ return transportConnection;
+ }
+ }
+
+ /**
+ * Disposes of the {@link TransportConnection}.
+ *
+ * @param transportConnection The {@link TransportConnection} to be disposed of.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ */
+ public void disposeOfTransportClient(TransportConnection transportConnection, String caller) {
+ transportConnection.unbind(caller);
+ transportConnection.markAsDisposed();
+ synchronized (mTransportClientsLock) {
+ TransportUtils.log(
+ Priority.DEBUG,
+ TAG,
+ formatMessage(null, caller, "Disposing of " + transportConnection));
+ mTransportClientsCallerMap.remove(transportConnection);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("Transport clients created: " + mTransportClientsCreated);
+ synchronized (mTransportClientsLock) {
+ pw.println("Current transport clients: " + mTransportClientsCallerMap.size());
+ for (TransportConnection transportConnection : mTransportClientsCallerMap.keySet()) {
+ String caller = mTransportClientsCallerMap.get(transportConnection);
+ pw.println(" " + transportConnection + " [" + caller + "]");
+ for (String logEntry : transportConnection.getLogBuffer()) {
+ pw.println(" " + logEntry);
+ }
+ }
+ }
+ }
+}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java
index c08eb7f..83b2782 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java
@@ -22,10 +22,10 @@
/**
* Exception thrown when the {@link IBackupTransport} is not available. This happen when a {@link
- * TransportClient} connection attempt fails. Check {@link
- * TransportClient#connectAsync(TransportConnectionListener, String)} for when that happens.
+ * TransportConnection} connection attempt fails. Check {@link
+ * TransportConnection#connectAsync(TransportConnectionListener, String)} for when that happens.
*
- * @see TransportClient#connectAsync(TransportConnectionListener, String)
+ * @see TransportConnection#connectAsync(TransportConnectionListener, String)
*/
public class TransportNotAvailableException extends AndroidException {
TransportNotAvailableException() {
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java
index bd84782..c67a5b65 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java
@@ -25,7 +25,7 @@
import java.util.Map;
import java.util.Optional;
-/** Responsible for aggregating {@link TransportClient} relevant times. */
+/** Responsible for aggregating {@link TransportConnection} relevant times. */
public class TransportStats {
private final Object mStatsLock = new Object();
private final Map<ComponentName, Stats> mTransportStats = new HashMap<>();
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 1a5d91c..452adb2 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -127,7 +127,7 @@
import com.android.server.backup.params.RestoreParams;
import com.android.server.backup.restore.ActiveRestoreSession;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.utils.BackupEligibilityRules;
@@ -1894,16 +1894,16 @@
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
- final TransportClient transportClient;
+ final TransportConnection transportConnection;
final String transportDirName;
int operationType;
try {
transportDirName =
mTransportManager.getTransportDirName(
mTransportManager.getCurrentTransportName());
- transportClient =
+ transportConnection =
mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
- operationType = getOperationTypeFromTransport(transportClient);
+ operationType = getOperationTypeFromTransport(transportConnection);
} catch (TransportNotRegisteredException | TransportNotAvailableException
| RemoteException e) {
BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
@@ -1914,13 +1914,13 @@
}
OnTaskFinishedListener listener =
- caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
+ caller -> mTransportManager.disposeOfTransportClient(transportConnection, caller);
BackupEligibilityRules backupEligibilityRules = getEligibilityRulesForOperation(
operationType);
Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
msg.obj = getRequestBackupParams(packages, observer, monitor, flags, backupEligibilityRules,
- transportClient, transportDirName, listener);
+ transportConnection, transportDirName, listener);
mBackupHandler.sendMessage(msg);
return BackupManager.SUCCESS;
}
@@ -1928,7 +1928,7 @@
@VisibleForTesting
BackupParams getRequestBackupParams(String[] packages, IBackupObserver observer,
IBackupManagerMonitor monitor, int flags, BackupEligibilityRules backupEligibilityRules,
- TransportClient transportClient, String transportDirName,
+ TransportConnection transportConnection, String transportDirName,
OnTaskFinishedListener listener) {
ArrayList<String> fullBackupList = new ArrayList<>();
ArrayList<String> kvBackupList = new ArrayList<>();
@@ -1974,7 +1974,7 @@
boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
- return new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
+ return new BackupParams(transportConnection, transportDirName, kvBackupList, fullBackupList,
observer, monitor, listener, /* userInitiated */ true, nonIncrementalBackup,
backupEligibilityRules);
}
@@ -2875,10 +2875,10 @@
}
mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
synchronized (mQueueLock) {
- TransportClient transportClient =
+ TransportConnection transportConnection =
mTransportManager
.getTransportClient(transportName, "BMS.clearBackupData()");
- if (transportClient == null) {
+ if (transportConnection == null) {
// transport is currently unregistered -- make sure to retry
Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
new ClearRetryParams(transportName, packageName));
@@ -2888,11 +2888,11 @@
final long oldId = Binder.clearCallingIdentity();
try {
OnTaskFinishedListener listener = caller -> mTransportManager
- .disposeOfTransportClient(transportClient, caller);
+ .disposeOfTransportClient(transportConnection, caller);
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(
MSG_RUN_CLEAR,
- new ClearParams(transportClient, info, listener));
+ new ClearParams(transportConnection, info, listener));
mBackupHandler.sendMessage(msg);
} finally {
Binder.restoreCallingIdentity(oldId);
@@ -3715,11 +3715,11 @@
// And update our current-dataset bookkeeping
String callerLogString = "BMS.updateStateForTransport()";
- TransportClient transportClient =
+ TransportConnection transportConnection =
mTransportManager.getTransportClient(newTransportName, callerLogString);
- if (transportClient != null) {
+ if (transportConnection != null) {
try {
- IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
+ IBackupTransport transport = transportConnection.connectOrThrow(callerLogString);
mCurrentToken = transport.getCurrentRestoreSet();
} catch (Exception e) {
// Oops. We can't know the current dataset token, so reset and figure it out
@@ -3733,7 +3733,7 @@
+ newTransportName
+ " not available: current token = 0"));
}
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ mTransportManager.disposeOfTransportClient(transportConnection, callerLogString);
} else {
Slog.w(
TAG,
@@ -3946,9 +3946,9 @@
skip = true;
}
- TransportClient transportClient =
+ TransportConnection transportConnection =
mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
- if (transportClient == null) {
+ if (transportConnection == null) {
if (DEBUG) Slog.w(TAG, addUserIdToLogMessage(mUserId, "No transport client"));
skip = true;
}
@@ -3972,7 +3972,7 @@
mWakelock.acquire();
OnTaskFinishedListener listener = caller -> {
- mTransportManager.disposeOfTransportClient(transportClient, caller);
+ mTransportManager.disposeOfTransportClient(transportConnection, caller);
mWakelock.release();
};
@@ -3984,7 +3984,7 @@
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj =
RestoreParams.createForRestoreAtInstall(
- transportClient,
+ transportConnection,
/* observer */ null,
/* monitor */ null,
restoreSet,
@@ -4006,9 +4006,9 @@
if (skip) {
// Auto-restore disabled or no way to attempt a restore
- if (transportClient != null) {
+ if (transportConnection != null) {
mTransportManager.disposeOfTransportClient(
- transportClient, "BMS.restoreAtInstall()");
+ transportConnection, "BMS.restoreAtInstall()");
}
// Tell the PackageManager to proceed with the post-install handling for this package.
@@ -4177,13 +4177,13 @@
final long oldToken = Binder.clearCallingIdentity();
try {
String callerLogString = "BMS.isAppEligibleForBackup";
- TransportClient transportClient =
+ TransportConnection transportConnection =
mTransportManager.getCurrentTransportClient(callerLogString);
boolean eligible =
mScheduledBackupEligibility.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName);
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ transportConnection, packageName);
+ if (transportConnection != null) {
+ mTransportManager.disposeOfTransportClient(transportConnection, callerLogString);
}
return eligible;
} finally {
@@ -4199,17 +4199,17 @@
final long oldToken = Binder.clearCallingIdentity();
try {
String callerLogString = "BMS.filterAppsEligibleForBackup";
- TransportClient transportClient =
+ TransportConnection transportConnection =
mTransportManager.getCurrentTransportClient(callerLogString);
List<String> eligibleApps = new LinkedList<>();
for (String packageName : packages) {
if (mScheduledBackupEligibility.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName)) {
+ transportConnection, packageName)) {
eligibleApps.add(packageName);
}
}
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ if (transportConnection != null) {
+ mTransportManager.disposeOfTransportClient(transportConnection, callerLogString);
}
return eligibleApps.toArray(new String[eligibleApps.size()]);
} finally {
@@ -4362,7 +4362,7 @@
}
@VisibleForTesting
- @OperationType int getOperationTypeFromTransport(TransportClient transportClient)
+ @OperationType int getOperationTypeFromTransport(TransportConnection transportConnection)
throws TransportNotAvailableException, RemoteException {
if (!shouldUseNewBackupEligibilityRules()) {
// Return the default to stick to the legacy behaviour.
@@ -4371,7 +4371,7 @@
final long oldCallingId = Binder.clearCallingIdentity();
try {
- IBackupTransport transport = transportClient.connectOrThrow(
+ IBackupTransport transport = transportConnection.connectOrThrow(
/* caller */ "BMS.getOperationTypeFromTransport");
if ((transport.getTransportFlags() & BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER) != 0) {
return OperationType.MIGRATION;
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 a4d47d4..1c86091 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -51,7 +51,7 @@
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
import com.android.server.backup.remote.RemoteCall;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
@@ -110,13 +110,15 @@
String caller,
BackupEligibilityRules backupEligibilityRules) {
TransportManager transportManager = backupManagerService.getTransportManager();
- TransportClient transportClient = transportManager.getCurrentTransportClient(caller);
+ TransportConnection transportConnection = transportManager.getCurrentTransportClient(
+ caller);
OnTaskFinishedListener listener =
listenerCaller ->
- transportManager.disposeOfTransportClient(transportClient, listenerCaller);
+ transportManager.disposeOfTransportClient(transportConnection,
+ listenerCaller);
return new PerformFullTransportBackupTask(
backupManagerService,
- transportClient,
+ transportConnection,
observer,
whichPackages,
updateSchedule,
@@ -145,7 +147,7 @@
SinglePackageBackupRunner mBackupRunner;
private final int mBackupRunnerOpToken;
private final OnTaskFinishedListener mListener;
- private final TransportClient mTransportClient;
+ private final TransportConnection mTransportConnection;
private final int mUserId;
// This is true when a backup operation for some package is in progress.
@@ -156,7 +158,7 @@
private final BackupEligibilityRules mBackupEligibilityRules;
public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
- TransportClient transportClient,
+ TransportConnection transportConnection,
IFullBackupRestoreObserver observer,
String[] whichPackages, boolean updateSchedule,
FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver,
@@ -164,7 +166,7 @@
boolean userInitiated, BackupEligibilityRules backupEligibilityRules) {
super(observer);
this.mUserBackupManagerService = backupManagerService;
- mTransportClient = transportClient;
+ mTransportConnection = transportConnection;
mUpdateSchedule = updateSchedule;
mLatch = latch;
mJob = runningJob;
@@ -299,7 +301,7 @@
try {
// If we're running a backup we should be connected to a transport
IBackupTransport transport =
- mTransportClient.getConnectedTransport("PFTBT.handleCancel()");
+ mTransportConnection.getConnectedTransport("PFTBT.handleCancel()");
transport.cancelFullBackup();
} catch (RemoteException | TransportNotAvailableException e) {
Slog.w(TAG, "Error calling cancelFullBackup() on transport: " + e);
@@ -351,7 +353,7 @@
return;
}
- IBackupTransport transport = mTransportClient.connect("PFTBT.run()");
+ IBackupTransport transport = mTransportConnection.connect("PFTBT.run()");
if (transport == null) {
Slog.w(TAG, "Transport not present; full data backup not performed");
backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
@@ -395,7 +397,7 @@
enginePipes = ParcelFileDescriptor.createPipe();
mBackupRunner =
new SinglePackageBackupRunner(enginePipes[1], currentPackage,
- mTransportClient, quota, mBackupRunnerOpToken,
+ mTransportConnection, quota, mBackupRunnerOpToken,
transport.getTransportFlags());
// The runner dup'd the pipe half, so we close it here
enginePipes[1].close();
@@ -697,17 +699,17 @@
class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
final AtomicLong mResult = new AtomicLong(BackupTransport.AGENT_ERROR);
final CountDownLatch mLatch = new CountDownLatch(1);
- final TransportClient mTransportClient;
+ final TransportConnection mTransportConnection;
final long mQuota;
private final int mCurrentOpToken;
private final int mTransportFlags;
SinglePackageBackupPreflight(
- TransportClient transportClient,
+ TransportConnection transportConnection,
long quota,
int currentOpToken,
int transportFlags) {
- mTransportClient = transportClient;
+ mTransportConnection = transportConnection;
mQuota = quota;
mCurrentOpToken = currentOpToken;
mTransportFlags = transportFlags;
@@ -744,7 +746,7 @@
}
IBackupTransport transport =
- mTransportClient.connectOrThrow("PFTBT$SPBP.preflightFullBackup()");
+ mTransportConnection.connectOrThrow("PFTBT$SPBP.preflightFullBackup()");
result = transport.checkFullBackupSize(totalSize);
if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
if (MORE_DEBUG) {
@@ -817,14 +819,14 @@
private final int mTransportFlags;
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
- TransportClient transportClient, long quota, int currentOpToken, int transportFlags)
- throws IOException {
+ TransportConnection transportConnection, long quota, int currentOpToken,
+ int transportFlags) throws IOException {
mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
mTarget = target;
mCurrentOpToken = currentOpToken;
mEphemeralToken = mUserBackupManagerService.generateRandomIntegerToken();
mPreflight = new SinglePackageBackupPreflight(
- transportClient, quota, mEphemeralToken, transportFlags);
+ transportConnection, quota, mEphemeralToken, transportFlags);
mPreflightLatch = new CountDownLatch(1);
mBackupLatch = new CountDownLatch(1);
mPreflightResult = BackupTransport.AGENT_ERROR;
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 1cb7c11..3b3bf8c 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -20,7 +20,6 @@
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
-import android.app.backup.BackupManager;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.RestoreSet;
import android.os.Handler;
@@ -52,7 +51,7 @@
import com.android.server.backup.params.RestoreParams;
import com.android.server.backup.restore.PerformAdbRestoreTask;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import java.util.ArrayList;
import java.util.Collections;
@@ -148,16 +147,16 @@
backupManagerService.setLastBackupPass(System.currentTimeMillis());
String callerLogString = "BH/MSG_RUN_BACKUP";
- TransportClient transportClient =
+ TransportConnection transportConnection =
transportManager.getCurrentTransportClient(callerLogString);
IBackupTransport transport =
- transportClient != null
- ? transportClient.connect(callerLogString)
+ transportConnection != null
+ ? transportConnection.connect(callerLogString)
: null;
if (transport == null) {
- if (transportClient != null) {
+ if (transportConnection != null) {
transportManager
- .disposeOfTransportClient(transportClient, callerLogString);
+ .disposeOfTransportClient(transportConnection, callerLogString);
}
Slog.v(TAG, "Backup requested but no transport available");
break;
@@ -212,10 +211,11 @@
OnTaskFinishedListener listener =
caller ->
transportManager
- .disposeOfTransportClient(transportClient, caller);
+ .disposeOfTransportClient(transportConnection,
+ caller);
KeyValueBackupTask.start(
backupManagerService,
- transportClient,
+ transportConnection,
transport.transportDirName(),
queue,
oldJournal,
@@ -240,7 +240,7 @@
}
if (!staged) {
- transportManager.disposeOfTransportClient(transportClient, callerLogString);
+ transportManager.disposeOfTransportClient(transportConnection, callerLogString);
// if we didn't actually hand off the wakelock, rewind until next time
synchronized (backupManagerService.getQueueLock()) {
backupManagerService.setBackupRunning(false);
@@ -296,7 +296,7 @@
PerformUnifiedRestoreTask task =
new PerformUnifiedRestoreTask(
backupManagerService,
- params.transportClient,
+ params.mTransportConnection,
params.observer,
params.monitor,
params.token,
@@ -344,7 +344,7 @@
Runnable task =
new PerformClearTask(
backupManagerService,
- params.transportClient,
+ params.mTransportConnection,
params.packageInfo,
params.listener);
task.run();
@@ -365,7 +365,7 @@
String callerLogString = "BH/MSG_RUN_GET_RESTORE_SETS";
try {
IBackupTransport transport =
- params.transportClient.connectOrThrow(callerLogString);
+ params.mTransportConnection.connectOrThrow(callerLogString);
sets = transport.getAvailableRestoreSets();
// cache the result in the active session
synchronized (params.session) {
@@ -459,7 +459,7 @@
KeyValueBackupTask.start(
backupManagerService,
- params.transportClient,
+ params.mTransportConnection,
params.dirName,
params.kvPackages,
/* dataChangedJournal */ null,
diff --git a/services/backup/java/com/android/server/backup/internal/OnTaskFinishedListener.java b/services/backup/java/com/android/server/backup/internal/OnTaskFinishedListener.java
index e417f06..30de509 100644
--- a/services/backup/java/com/android/server/backup/internal/OnTaskFinishedListener.java
+++ b/services/backup/java/com/android/server/backup/internal/OnTaskFinishedListener.java
@@ -16,7 +16,7 @@
package com.android.server.backup.internal;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportConnectionListener;
/** Listener to be called when a task finishes, successfully or not. */
@@ -27,7 +27,7 @@
* Called when a task finishes, successfully or not.
*
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * {@link TransportConnection#connectAsync(TransportConnectionListener, String)} for more
* details.
*/
void onFinished(String caller);
diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
index 5ffa795..80bd604 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
@@ -24,23 +24,23 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import java.io.File;
public class PerformClearTask implements Runnable {
private final UserBackupManagerService mBackupManagerService;
private final TransportManager mTransportManager;
- private final TransportClient mTransportClient;
+ private final TransportConnection mTransportConnection;
private final PackageInfo mPackage;
private final OnTaskFinishedListener mListener;
PerformClearTask(UserBackupManagerService backupManagerService,
- TransportClient transportClient, PackageInfo packageInfo,
+ TransportConnection transportConnection, PackageInfo packageInfo,
OnTaskFinishedListener listener) {
mBackupManagerService = backupManagerService;
mTransportManager = backupManagerService.getTransportManager();
- mTransportClient = transportClient;
+ mTransportConnection = transportConnection;
mPackage = packageInfo;
mListener = listener;
}
@@ -51,12 +51,13 @@
try {
// Clear the on-device backup state to ensure a full backup next time
String transportDirName =
- mTransportManager.getTransportDirName(mTransportClient.getTransportComponent());
+ mTransportManager.getTransportDirName(
+ mTransportConnection.getTransportComponent());
File stateDir = new File(mBackupManagerService.getBaseStateDir(), transportDirName);
File stateFile = new File(stateDir, mPackage.packageName);
stateFile.delete();
- transport = mTransportClient.connectOrThrow(callerLogString);
+ transport = mTransportConnection.connectOrThrow(callerLogString);
// Tell the transport to remove all the persistent storage for the app
// TODO - need to handle failures
transport.clearBackupData(mPackage);
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 6b78fbf..7636ef6 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -32,7 +32,7 @@
import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import java.io.File;
import java.util.ArrayList;
@@ -109,26 +109,26 @@
public void run() {
// mWakelock is *acquired* when execution begins here
String callerLogString = "PerformInitializeTask.run()";
- List<TransportClient> transportClientsToDisposeOf = new ArrayList<>(mQueue.length);
+ List<TransportConnection> transportClientsToDisposeOf = new ArrayList<>(mQueue.length);
int result = BackupTransport.TRANSPORT_OK;
try {
for (String transportName : mQueue) {
- TransportClient transportClient =
+ TransportConnection transportConnection =
mTransportManager.getTransportClient(transportName, callerLogString);
- if (transportClient == null) {
+ if (transportConnection == null) {
Slog.e(TAG, "Requested init for " + transportName + " but not found");
continue;
}
- transportClientsToDisposeOf.add(transportClient);
+ transportClientsToDisposeOf.add(transportConnection);
Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
String transportDirName =
mTransportManager.getTransportDirName(
- transportClient.getTransportComponent());
+ transportConnection.getTransportComponent());
EventLog.writeEvent(EventLogTags.BACKUP_START, transportDirName);
long startRealtime = SystemClock.elapsedRealtime();
- IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
+ IBackupTransport transport = transportConnection.connectOrThrow(callerLogString);
int status = transport.initializeDevice();
if (status != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Transport error in initializeDevice()");
@@ -170,8 +170,8 @@
Slog.e(TAG, "Unexpected error performing init", e);
result = BackupTransport.TRANSPORT_ERROR;
} finally {
- for (TransportClient transportClient : transportClientsToDisposeOf) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ for (TransportConnection transportConnection : transportClientsToDisposeOf) {
+ mTransportManager.disposeOfTransportClient(transportConnection, callerLogString);
}
notifyFinished(result);
mListener.onFinished(callerLogString);
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 7267cdf..bdb2e6f 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -65,7 +65,7 @@
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.remote.RemoteCallable;
import com.android.server.backup.remote.RemoteResult;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.utils.BackupEligibilityRules;
@@ -192,10 +192,10 @@
* dedicated thread and kicks off the operation in it.
*
* @param backupManagerService The {@link UserBackupManagerService} instance.
- * @param transportClient The {@link TransportClient} that contains the transport used for the
- * operation.
+ * @param transportConnection The {@link TransportConnection} that contains the transport used
+ * for the operation.
* @param transportDirName The value of {@link IBackupTransport#transportDirName()} for the
- * transport whose {@link TransportClient} was provided above.
+ * transport whose {@link TransportConnection} was provided above.
* @param queue The list of package names that will be backed-up.
* @param dataChangedJournal The old data-changed journal file that will be deleted when the
* operation finishes (successfully or not) or {@code null}.
@@ -211,7 +211,7 @@
*/
public static KeyValueBackupTask start(
UserBackupManagerService backupManagerService,
- TransportClient transportClient,
+ TransportConnection transportConnection,
String transportDirName,
List<String> queue,
@Nullable DataChangedJournal dataChangedJournal,
@@ -227,7 +227,7 @@
KeyValueBackupTask task =
new KeyValueBackupTask(
backupManagerService,
- transportClient,
+ transportConnection,
transportDirName,
queue,
dataChangedJournal,
@@ -245,7 +245,7 @@
private final UserBackupManagerService mBackupManagerService;
private final PackageManager mPackageManager;
- private final TransportClient mTransportClient;
+ private final TransportConnection mTransportConnection;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final KeyValueBackupReporter mReporter;
private final OnTaskFinishedListener mTaskFinishedListener;
@@ -302,7 +302,7 @@
@VisibleForTesting
public KeyValueBackupTask(
UserBackupManagerService backupManagerService,
- TransportClient transportClient,
+ TransportConnection transportConnection,
String transportDirName,
List<String> queue,
@Nullable DataChangedJournal journal,
@@ -314,7 +314,7 @@
BackupEligibilityRules backupEligibilityRules) {
mBackupManagerService = backupManagerService;
mPackageManager = backupManagerService.getPackageManager();
- mTransportClient = transportClient;
+ mTransportConnection = transportConnection;
mOriginalQueue = queue;
// We need to retain the original queue contents in case of transport failure
mQueue = new ArrayList<>(queue);
@@ -418,7 +418,7 @@
boolean noDataPackageEncountered = false;
try {
IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.informTransportOfEmptyBackups()");
+ mTransportConnection.connectOrThrow("KVBT.informTransportOfEmptyBackups()");
for (String packageName : succeedingPackages) {
if (appsBackedUp.contains(packageName)) {
@@ -463,7 +463,7 @@
private boolean isEligibleForNoDataCall(PackageInfo packageInfo) {
return mBackupEligibilityRules.appIsKeyValueOnly(packageInfo)
&& mBackupEligibilityRules.appIsRunningAndEligibleForBackupWithTransport(
- mTransportClient, packageInfo.packageName);
+ mTransportConnection, packageInfo.packageName);
}
/** Send the "no data changed" message to a transport for a specific package */
@@ -608,7 +608,7 @@
mReporter.onQueueReady(mQueue);
File pmState = new File(mStateDirectory, PM_PACKAGE);
try {
- IBackupTransport transport = mTransportClient.connectOrThrow("KVBT.startTask()");
+ IBackupTransport transport = mTransportConnection.connectOrThrow("KVBT.startTask()");
String transportName = transport.name();
if (transportName.contains("EncryptedLocalTransport")) {
// Temporary code for EiTF POC. Only supports non-incremental backups.
@@ -638,7 +638,7 @@
private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) {
return new PerformFullTransportBackupTask(
mBackupManagerService,
- mTransportClient,
+ mTransportConnection,
/* fullBackupRestoreObserver */ null,
packages.toArray(new String[packages.size()]),
/* updateSchedule */ false,
@@ -764,7 +764,7 @@
long currentToken = mBackupManagerService.getCurrentToken();
if (mHasDataToBackup && (status == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
try {
- IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
+ IBackupTransport transport = mTransportConnection.connectOrThrow(callerLogString);
transportName = transport.name();
mBackupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
mBackupManagerService.writeRestoreTokens();
@@ -836,7 +836,7 @@
@GuardedBy("mQueueLock")
private void triggerTransportInitializationLocked() throws Exception {
IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked");
+ mTransportConnection.connectOrThrow("KVBT.triggerTransportInitializationLocked");
mBackupManagerService.getPendingInits().add(transport.name());
deletePmStateFile();
mBackupManagerService.backupNow();
@@ -919,7 +919,8 @@
}
}
- IBackupTransport transport = mTransportClient.connectOrThrow("KVBT.extractAgentData()");
+ IBackupTransport transport = mTransportConnection.connectOrThrow(
+ "KVBT.extractAgentData()");
long quota = transport.getBackupQuota(packageName, /* isFullBackup */ false);
int transportFlags = transport.getTransportFlags();
@@ -1078,7 +1079,7 @@
try (ParcelFileDescriptor backupData =
ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.transportPerformBackup()");
+ mTransportConnection.connectOrThrow("KVBT.transportPerformBackup()");
mReporter.onTransportPerformBackup(packageName);
int flags = getPerformBackupFlags(mUserInitiated, nonIncremental);
@@ -1131,7 +1132,7 @@
if (agent != null) {
try {
IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.agentDoQuotaExceeded()");
+ mTransportConnection.connectOrThrow("KVBT.agentDoQuotaExceeded()");
long quota = transport.getBackupQuota(packageName, false);
remoteCall(
callback -> agent.doQuotaExceeded(size, quota, callback),
@@ -1227,7 +1228,7 @@
long delay;
try {
IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.revertTask()");
+ mTransportConnection.connectOrThrow("KVBT.revertTask()");
delay = transport.requestBackupTime();
} catch (Exception e) {
mReporter.onTransportRequestBackupTimeError(e);
diff --git a/services/backup/java/com/android/server/backup/params/BackupParams.java b/services/backup/java/com/android/server/backup/params/BackupParams.java
index 8002570..c8ed00b 100644
--- a/services/backup/java/com/android/server/backup/params/BackupParams.java
+++ b/services/backup/java/com/android/server/backup/params/BackupParams.java
@@ -20,14 +20,14 @@
import android.app.backup.IBackupObserver;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
import java.util.ArrayList;
public class BackupParams {
- public TransportClient transportClient;
+ public TransportConnection mTransportConnection;
public String dirName;
public ArrayList<String> kvPackages;
public ArrayList<String> fullPackages;
@@ -38,11 +38,11 @@
public boolean nonIncrementalBackup;
public BackupEligibilityRules mBackupEligibilityRules;
- public BackupParams(TransportClient transportClient, String dirName,
+ public BackupParams(TransportConnection transportConnection, String dirName,
ArrayList<String> kvPackages, ArrayList<String> fullPackages, IBackupObserver observer,
IBackupManagerMonitor monitor, OnTaskFinishedListener listener, boolean userInitiated,
boolean nonIncrementalBackup, BackupEligibilityRules backupEligibilityRules) {
- this.transportClient = transportClient;
+ this.mTransportConnection = transportConnection;
this.dirName = dirName;
this.kvPackages = kvPackages;
this.fullPackages = fullPackages;
diff --git a/services/backup/java/com/android/server/backup/params/ClearParams.java b/services/backup/java/com/android/server/backup/params/ClearParams.java
index dc3bba0..bc3b79b 100644
--- a/services/backup/java/com/android/server/backup/params/ClearParams.java
+++ b/services/backup/java/com/android/server/backup/params/ClearParams.java
@@ -19,18 +19,18 @@
import android.content.pm.PackageInfo;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
public class ClearParams {
- public TransportClient transportClient;
+ public TransportConnection mTransportConnection;
public PackageInfo packageInfo;
public OnTaskFinishedListener listener;
public ClearParams(
- TransportClient transportClient,
+ TransportConnection transportConnection,
PackageInfo packageInfo,
OnTaskFinishedListener listener) {
- this.transportClient = transportClient;
+ this.mTransportConnection = transportConnection;
this.packageInfo = packageInfo;
this.listener = listener;
}
diff --git a/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java b/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
index 914e9ea..dbd06ee 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
@@ -19,22 +19,21 @@
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IRestoreObserver;
-import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.restore.ActiveRestoreSession;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
public class RestoreGetSetsParams {
- public final TransportClient transportClient;
+ public final TransportConnection mTransportConnection;
public final ActiveRestoreSession session;
public final IRestoreObserver observer;
public final IBackupManagerMonitor monitor;
public final OnTaskFinishedListener listener;
- public RestoreGetSetsParams(TransportClient _transportClient, ActiveRestoreSession _session,
- IRestoreObserver _observer, IBackupManagerMonitor _monitor,
- OnTaskFinishedListener _listener) {
- transportClient = _transportClient;
+ public RestoreGetSetsParams(TransportConnection _transportConnection,
+ ActiveRestoreSession _session, IRestoreObserver _observer,
+ IBackupManagerMonitor _monitor, OnTaskFinishedListener _listener) {
+ mTransportConnection = _transportConnection;
session = _session;
observer = _observer;
monitor = _monitor;
diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java
index a08a1f8..1795a3cb 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java
@@ -22,14 +22,11 @@
import android.content.pm.PackageInfo;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
-import java.util.Map;
-import java.util.Set;
-
public class RestoreParams {
- public final TransportClient transportClient;
+ public final TransportConnection mTransportConnection;
public final IRestoreObserver observer;
public final IBackupManagerMonitor monitor;
public final long token;
@@ -44,7 +41,7 @@
* No kill after restore.
*/
public static RestoreParams createForSinglePackage(
- TransportClient transportClient,
+ TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
long token,
@@ -52,7 +49,7 @@
OnTaskFinishedListener listener,
BackupEligibilityRules eligibilityRules) {
return new RestoreParams(
- transportClient,
+ transportConnection,
observer,
monitor,
token,
@@ -68,7 +65,7 @@
* Kill after restore.
*/
public static RestoreParams createForRestoreAtInstall(
- TransportClient transportClient,
+ TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
long token,
@@ -78,7 +75,7 @@
BackupEligibilityRules backupEligibilityRules) {
String[] filterSet = {packageName};
return new RestoreParams(
- transportClient,
+ transportConnection,
observer,
monitor,
token,
@@ -94,14 +91,14 @@
* This is the form that Setup Wizard or similar restore UXes use.
*/
public static RestoreParams createForRestoreAll(
- TransportClient transportClient,
+ TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
long token,
OnTaskFinishedListener listener,
BackupEligibilityRules backupEligibilityRules) {
return new RestoreParams(
- transportClient,
+ transportConnection,
observer,
monitor,
token,
@@ -117,7 +114,7 @@
* Caller specifies whether is considered a system-level restore.
*/
public static RestoreParams createForRestorePackages(
- TransportClient transportClient,
+ TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
long token,
@@ -126,7 +123,7 @@
OnTaskFinishedListener listener,
BackupEligibilityRules backupEligibilityRules) {
return new RestoreParams(
- transportClient,
+ transportConnection,
observer,
monitor,
token,
@@ -139,7 +136,7 @@
}
private RestoreParams(
- TransportClient transportClient,
+ TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
long token,
@@ -149,7 +146,7 @@
@Nullable String[] filterSet,
OnTaskFinishedListener listener,
BackupEligibilityRules backupEligibilityRules) {
- this.transportClient = transportClient;
+ this.mTransportConnection = transportConnection;
this.observer = observer;
this.monitor = monitor;
this.token = token;
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index d0a8881..8b1d561 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -26,7 +26,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
@@ -44,7 +43,7 @@
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.params.RestoreGetSetsParams;
import com.android.server.backup.params.RestoreParams;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
import java.util.function.BiFunction;
@@ -104,10 +103,10 @@
final long oldId = Binder.clearCallingIdentity();
try {
- TransportClient transportClient =
+ TransportConnection transportConnection =
mTransportManager.getTransportClient(
mTransportName, "RestoreSession.getAvailableRestoreSets()");
- if (transportClient == null) {
+ if (transportConnection == null) {
Slog.w(TAG, "Null transport client getting restore sets");
return -1;
}
@@ -123,12 +122,13 @@
// Prevent lambda from leaking 'this'
TransportManager transportManager = mTransportManager;
OnTaskFinishedListener listener = caller -> {
- transportManager.disposeOfTransportClient(transportClient, caller);
+ transportManager.disposeOfTransportClient(transportConnection, caller);
wakelock.release();
};
Message msg = mBackupManagerService.getBackupHandler().obtainMessage(
MSG_RUN_GET_RESTORE_SETS,
- new RestoreGetSetsParams(transportClient, this, observer, monitor, listener));
+ new RestoreGetSetsParams(transportConnection, this, observer, monitor,
+ listener));
mBackupManagerService.getBackupHandler().sendMessage(msg);
return 0;
} catch (Exception e) {
@@ -399,11 +399,11 @@
* Returns 0 if operation sent or -1 otherwise.
*/
private int sendRestoreToHandlerLocked(
- BiFunction<TransportClient, OnTaskFinishedListener, RestoreParams> restoreParamsBuilder,
- String callerLogString) {
- TransportClient transportClient =
+ BiFunction<TransportConnection, OnTaskFinishedListener,
+ RestoreParams> restoreParamsBuilder, String callerLogString) {
+ TransportConnection transportConnection =
mTransportManager.getTransportClient(mTransportName, callerLogString);
- if (transportClient == null) {
+ if (transportConnection == null) {
Slog.e(TAG, "Transport " + mTransportName + " got unregistered");
return -1;
}
@@ -421,11 +421,11 @@
// Prevent lambda from leaking 'this'
TransportManager transportManager = mTransportManager;
OnTaskFinishedListener listener = caller -> {
- transportManager.disposeOfTransportClient(transportClient, caller);
+ transportManager.disposeOfTransportClient(transportConnection, caller);
wakelock.release();
};
Message msg = backupHandler.obtainMessage(MSG_RUN_RESTORE);
- msg.obj = restoreParamsBuilder.apply(transportClient, listener);
+ msg.obj = restoreParamsBuilder.apply(transportConnection, listener);
backupHandler.sendMessage(msg);
return 0;
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index f07bac8..8c786d5 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -66,7 +66,7 @@
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
@@ -87,7 +87,7 @@
private final int mUserId;
private final TransportManager mTransportManager;
// Transport client we're working with to do the restore
- private final TransportClient mTransportClient;
+ private final TransportConnection mTransportConnection;
// Where per-transport saved state goes
private File mStateDir;
@@ -169,7 +169,7 @@
PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
mListener = null;
mAgentTimeoutParameters = null;
- mTransportClient = null;
+ mTransportConnection = null;
mTransportManager = null;
mEphemeralOpToken = 0;
mUserId = 0;
@@ -181,7 +181,7 @@
// about releasing it.
public PerformUnifiedRestoreTask(
UserBackupManagerService backupManagerService,
- TransportClient transportClient,
+ TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
long restoreSetToken,
@@ -198,7 +198,7 @@
mState = UnifiedRestoreState.INITIAL;
mStartRealtime = SystemClock.elapsedRealtime();
- mTransportClient = transportClient;
+ mTransportConnection = transportConnection;
mObserver = observer;
mMonitor = monitor;
mToken = restoreSetToken;
@@ -386,7 +386,8 @@
try {
String transportDirName =
- mTransportManager.getTransportDirName(mTransportClient.getTransportComponent());
+ mTransportManager.getTransportDirName(
+ mTransportConnection.getTransportComponent());
mStateDir = new File(backupManagerService.getBaseStateDir(), transportDirName);
// Fetch the current metadata from the dataset first
@@ -397,7 +398,7 @@
PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
IBackupTransport transport =
- mTransportClient.connectOrThrow("PerformUnifiedRestoreTask.startRestore()");
+ mTransportConnection.connectOrThrow("PerformUnifiedRestoreTask.startRestore()");
mStatus = transport.startRestore(mToken, packages);
if (mStatus != BackupTransport.TRANSPORT_OK) {
@@ -495,7 +496,7 @@
UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
try {
IBackupTransport transport =
- mTransportClient.connectOrThrow(
+ mTransportConnection.connectOrThrow(
"PerformUnifiedRestoreTask.dispatchNextRestore()");
mRestoreDescription = transport.nextRestorePackage();
final String pkgName = (mRestoreDescription != null)
@@ -709,7 +710,7 @@
try {
IBackupTransport transport =
- mTransportClient.connectOrThrow(
+ mTransportConnection.connectOrThrow(
"PerformUnifiedRestoreTask.initiateOneRestore()");
// Run the transport's restore pass
@@ -939,7 +940,7 @@
String callerLogString = "PerformUnifiedRestoreTask$StreamFeederThread.run()";
try {
- IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
+ IBackupTransport transport = mTransportConnection.connectOrThrow(callerLogString);
while (status == BackupTransport.TRANSPORT_OK) {
// have the transport write some of the restoring data to us
int result = transport.getNextFullRestoreDataChunk(tWriteEnd);
@@ -1032,7 +1033,7 @@
// level is immaterial; we need to tell the transport to bail
try {
IBackupTransport transport =
- mTransportClient.connectOrThrow(callerLogString);
+ mTransportConnection.connectOrThrow(callerLogString);
transport.abortFullRestore();
} catch (Exception e) {
// transport itself is dead; make sure we handle this as a
@@ -1095,7 +1096,7 @@
String callerLogString = "PerformUnifiedRestoreTask.finalizeRestore()";
try {
IBackupTransport transport =
- mTransportClient.connectOrThrow(callerLogString);
+ mTransportConnection.connectOrThrow(callerLogString);
transport.finishRestore();
} catch (Exception e) {
Slog.e(TAG, "Error finishing restore", e);
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index bfb6f65..652386f 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -42,11 +42,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.ArrayUtils;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.google.android.collect.Sets;
-import java.util.Objects;
import java.util.Set;
/**
@@ -225,7 +224,7 @@
* </ol>
*/
public boolean appIsRunningAndEligibleForBackupWithTransport(
- @Nullable TransportClient transportClient,
+ @Nullable TransportConnection transportConnection,
String packageName) {
try {
PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
@@ -236,10 +235,10 @@
|| appIsDisabled(applicationInfo)) {
return false;
}
- if (transportClient != null) {
+ if (transportConnection != null) {
try {
IBackupTransport transport =
- transportClient.connectOrThrow(
+ transportConnection.connectOrThrow(
"AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport");
return transport.isAppEligibleForBackup(
packageInfo, appGetsFullBackup(packageInfo));
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 2645f3f..a0a00f7 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -16,29 +16,23 @@
package com.android.server.companion;
-import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
-import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
-import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
import static com.android.internal.util.CollectionUtils.filter;
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
-import static com.android.server.companion.CompanionDeviceManagerService.getCallingUserId;
+import static com.android.server.companion.PermissionsUtils.enforceCallerPermissionsToRequest;
+import static com.android.server.companion.RolesUtils.isRoleHolder;
-import static java.util.Collections.unmodifiableMap;
+import static java.util.Objects.requireNonNull;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.role.RoleManager;
+import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
+import android.companion.IAssociationRequestCallback;
import android.companion.ICompanionDeviceDiscoveryService;
-import android.companion.IFindDeviceCallback;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -46,8 +40,6 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.PackageUtils;
import android.util.Slog;
@@ -60,26 +52,12 @@
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
class AssociationRequestsProcessor {
private static final String TAG = LOG_TAG + ".AssociationRequestsProcessor";
- private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION;
- static {
- final Map<String, String> map = new ArrayMap<>();
- map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
- map.put(DEVICE_PROFILE_APP_STREAMING,
- Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING);
- map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
- Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION);
-
- DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
- }
-
private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
".CompanionDeviceDiscoveryService");
@@ -89,57 +67,91 @@
private final Context mContext;
private final CompanionDeviceManagerService mService;
+ private final PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
private AssociationRequest mRequest;
- private IFindDeviceCallback mFindDeviceCallback;
- private String mCallingPackage;
+ private IAssociationRequestCallback mAppCallback;
private AndroidFuture<?> mOngoingDeviceDiscovery;
- private RoleManager mRoleManager;
- private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
-
- AssociationRequestsProcessor(CompanionDeviceManagerService service, RoleManager roleManager) {
+ AssociationRequestsProcessor(CompanionDeviceManagerService service) {
mContext = service.getContext();
mService = service;
- mRoleManager = roleManager;
final Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
- mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
+ mServiceConnectors = new PerUser<>() {
@Override
protected ServiceConnector<ICompanionDeviceDiscoveryService> create(int userId) {
return new ServiceConnector.Impl<>(
- mContext,
- serviceIntent, 0/* bindingFlags */, userId,
- ICompanionDeviceDiscoveryService.Stub::asInterface);
+ mContext,
+ serviceIntent, 0/* bindingFlags */, userId,
+ ICompanionDeviceDiscoveryService.Stub::asInterface);
}
};
}
- void process(AssociationRequest request, IFindDeviceCallback callback, String callingPackage)
- throws RemoteException {
+ /**
+ * Handle incoming {@link AssociationRequest}s, sent via
+ * {@link android.companion.ICompanionDeviceManager#associate(AssociationRequest, IAssociationRequestCallback, String, int)}
+ */
+ void process(@NonNull AssociationRequest request, @NonNull String packageName,
+ @UserIdInt int userId, @NonNull IAssociationRequestCallback callback) {
+ requireNonNull(request, "Request MUST NOT be null");
+ if (request.isSelfManaged()) {
+ requireNonNull(request.getDisplayName(), "AssociationRequest.displayName "
+ + "MUST NOT be null.");
+ }
+ requireNonNull(packageName, "Package name MUST NOT be null");
+ requireNonNull(callback, "Callback MUST NOT be null");
+
if (DEBUG) {
- Slog.d(TAG, "process(request=" + request + ", from=" + callingPackage + ")");
+ Slog.d(TAG, "process() "
+ + "request=" + request + ", "
+ + "package=u" + userId + "/" + packageName);
}
- checkNotNull(request, "Request cannot be null");
- checkNotNull(callback, "Callback cannot be null");
- mService.checkCallerIsSystemOr(callingPackage);
- int userId = getCallingUserId();
- mService.checkUsesFeature(callingPackage, userId);
- final String deviceProfile = request.getDeviceProfile();
- validateDeviceProfileAndCheckPermission(deviceProfile);
+ // 1. Enforce permissions and other requirements.
+ enforceCallerPermissionsToRequest(mContext, request, packageName, userId);
+ mService.checkUsesFeature(packageName, userId);
- mFindDeviceCallback = callback;
- mRequest = request;
- mCallingPackage = callingPackage;
- request.setCallingPackage(callingPackage);
+ // 2. Check if association can be created without launching UI (i.e. CDM needs NEITHER
+ // to perform discovery NOR to collect user consent).
+ if (request.isSelfManaged() && !request.isForceConfirmation()
+ && !willAddRoleHolder(request, packageName, userId)) {
+ // 2a. Create association right away.
+ final AssociationInfo association = mService.createAssociation(userId, packageName,
+ /* macAddress */ null, request.getDisplayName(), request.getDeviceProfile(),
+ /* selfManaged */true);
+ withCatchingRemoteException(() -> callback.onAssociationCreated(association));
+ return;
+ }
- if (mayAssociateWithoutPrompt(callingPackage, userId)) {
+ // 2b. Launch the UI.
+ synchronized (mService.mLock) {
+ if (mRequest != null) {
+ Slog.w(TAG, "CDM is already processing another AssociationRequest.");
+
+ withCatchingRemoteException(() -> callback.onFailure("Busy."));
+ }
+
+ final boolean linked = withCatchingRemoteException(
+ () -> callback.asBinder().linkToDeath(mBinderDeathRecipient, 0));
+ if (!linked) {
+ // The process has died by now: do not proceed.
+ return;
+ }
+
+ mRequest = request;
+ }
+
+ mAppCallback = callback;
+ request.setCallingPackage(packageName);
+
+ if (mayAssociateWithoutPrompt(packageName, userId)) {
Slog.i(TAG, "setSkipPrompt(true)");
request.setSkipPrompt(true);
}
- callback.asBinder().linkToDeath(mBinderDeathRecipient /* recipient */, 0);
+ final String deviceProfile = request.getDeviceProfile();
mOngoingDeviceDiscovery = getDeviceProfilePermissionDescription(deviceProfile)
.thenComposeAsync(description -> {
if (DEBUG) {
@@ -155,17 +167,16 @@
}
AndroidFuture<String> future = new AndroidFuture<>();
- service.startDiscovery(request, callingPackage, callback, future);
+ service.startDiscovery(request, packageName, callback, future);
return future;
}).cancelTimeout();
}, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> {
if (err == null) {
- mService.createAssociationInternal(
- userId, deviceAddress, callingPackage, deviceProfile);
- mServiceConnectors.forUser(userId).post(service -> {
- service.onAssociationCreated();
- });
+ mService.legacyCreateAssociation(
+ userId, deviceAddress, packageName, deviceProfile);
+ mServiceConnectors.forUser(userId).post(
+ ICompanionDeviceDiscoveryService::onAssociationCreated);
} else {
Slog.e(TAG, "Failed to discover device(s)", err);
callback.onFailure("No devices found: " + err.getMessage());
@@ -174,52 +185,16 @@
}));
}
- private boolean isRoleHolder(int userId, String packageName, String role) {
- final long identity = Binder.clearCallingIdentity();
- try {
- List<String> holders = mRoleManager.getRoleHoldersAsUser(role, UserHandle.of(userId));
- return holders.contains(packageName);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ private boolean willAddRoleHolder(@NonNull AssociationRequest request,
+ @NonNull String packageName, @UserIdInt int userId) {
+ final String deviceProfile = request.getDeviceProfile();
+ if (deviceProfile == null) return false;
- void stopScan(AssociationRequest request, IFindDeviceCallback callback, String callingPackage) {
- if (DEBUG) {
- Slog.d(TAG, "stopScan(request = " + request + ")");
- }
- if (Objects.equals(request, mRequest)
- && Objects.equals(callback, mFindDeviceCallback)
- && Objects.equals(callingPackage, mCallingPackage)) {
- cleanup();
- }
- }
+ final boolean isRoleHolder = Binder.withCleanCallingIdentity(
+ () -> isRoleHolder(mContext, userId, packageName, deviceProfile));
- private void validateDeviceProfileAndCheckPermission(@Nullable String deviceProfile) {
- // Device profile can be null.
- if (deviceProfile == null) return;
-
- if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
- }
-
- if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_AUTOMOTIVE_PROJECTION is not fully supported yet.");
- }
-
- if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
- throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
- }
-
- final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
- if (mContext.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
- throw new SecurityException("Application must hold " + permission + " to associate "
- + "with a device with " + deviceProfile + " profile.");
- }
+ // Don't need to "grant" the role, if the package already holds the role.
+ return !isRoleHolder;
}
private void cleanup() {
@@ -232,20 +207,23 @@
if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) {
ongoingDeviceDiscovery.cancel(true);
}
- if (mFindDeviceCallback != null) {
- mFindDeviceCallback.asBinder().unlinkToDeath(mBinderDeathRecipient, 0);
- mFindDeviceCallback = null;
+ if (mAppCallback != null) {
+ mAppCallback.asBinder().unlinkToDeath(mBinderDeathRecipient, 0);
+ mAppCallback = null;
}
mRequest = null;
- mCallingPackage = null;
}
}
private boolean mayAssociateWithoutPrompt(String packageName, int userId) {
- if (mRequest.getDeviceProfile() != null
- && isRoleHolder(userId, packageName, mRequest.getDeviceProfile())) {
- // Don't need to collect user's consent since app already holds the role.
- return true;
+ final String deviceProfile = mRequest.getDeviceProfile();
+ if (deviceProfile != null) {
+ final boolean isRoleHolder = Binder.withCleanCallingIdentity(
+ () -> isRoleHolder(mContext, userId, packageName, deviceProfile));
+ if (isRoleHolder) {
+ // Don't need to collect user's consent since app already holds the role.
+ return true;
+ }
}
String[] sameOemPackages = mContext.getResources()
@@ -261,7 +239,7 @@
// Throttle frequent associations
long now = System.currentTimeMillis();
Set<AssociationInfo> recentAssociations = filter(
- mService.getAllAssociations(userId, packageName),
+ mService.getAssociations(userId, packageName),
a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS);
if (recentAssociations.size() >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) {
@@ -351,4 +329,17 @@
return sameOemPackageCerts;
}
+
+ private static boolean withCatchingRemoteException(ThrowingRunnable runnable) {
+ try {
+ runnable.run();
+ } catch (RemoteException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private interface ThrowingRunnable {
+ void run() throws RemoteException;
+ }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index d5357dc..049018cf 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,11 +17,15 @@
package com.android.server.companion;
+import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
-import static android.companion.DeviceId.TYPE_MAC_ADDRESS;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Binder.getCallingUid;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.UserHandle.getCallingUserId;
import static com.android.internal.util.CollectionUtils.add;
import static com.android.internal.util.CollectionUtils.any;
@@ -29,11 +33,16 @@
import static com.android.internal.util.CollectionUtils.find;
import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.internal.util.CollectionUtils.map;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import static com.android.server.companion.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
+import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
+import static com.android.server.companion.PermissionsUtils.enforceCallerCanInteractWithUserId;
+import static com.android.server.companion.PermissionsUtils.enforceCallerCanManagerCompanionDevice;
+import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
+import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
+import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
@@ -48,7 +57,6 @@
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.role.RoleManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
@@ -58,10 +66,10 @@
import android.bluetooth.le.ScanSettings;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
-import android.companion.DeviceId;
import android.companion.DeviceNotAssociatedException;
+import android.companion.IAssociationRequestCallback;
import android.companion.ICompanionDeviceManager;
-import android.companion.IFindDeviceCallback;
+import android.companion.IOnAssociationsChangedListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -74,13 +82,13 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.net.MacAddress;
import android.net.NetworkPolicyManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.Parcel;
import android.os.PowerWhitelistManager;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -117,7 +125,6 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -159,7 +166,6 @@
private final AssociationRequestsProcessor mAssociationRequestsProcessor;
private PowerWhitelistManager mPowerWhitelistManager;
private IAppOpsService mAppOpsManager;
- private RoleManager mRoleManager;
private BluetoothAdapter mBluetoothAdapter;
private UserManager mUserManager;
@@ -202,7 +208,6 @@
mPersistentDataStore = new PersistentDataStore();
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
- mRoleManager = context.getSystemService(RoleManager.class);
mAppOpsManager = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
@@ -212,7 +217,7 @@
context.getSystemService(PermissionControllerManager.class));
mUserManager = context.getSystemService(UserManager.class);
mCompanionDevicePresenceController = new CompanionDevicePresenceController();
- mAssociationRequestsProcessor = new AssociationRequestsProcessor(this, mRoleManager);
+ mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
registerPackageMonitor();
}
@@ -221,26 +226,28 @@
new PackageMonitor() {
@Override
public void onPackageRemoved(String packageName, int uid) {
- Slog.d(LOG_TAG, "onPackageRemoved(packageName = " + packageName
- + ", uid = " + uid + ")");
- int userId = getChangingUserId();
- updateAssociations(
- set -> filterOut(set, it -> it.belongsToPackage(userId, packageName)),
- userId);
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageRemoved() u" + userId + "/" + packageName);
- mCompanionDevicePresenceController.unbindDevicePresenceListener(
- packageName, userId);
+ clearAssociationForPackage(userId, packageName);
+ }
+
+ @Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageDataCleared() u" + userId + "/" + packageName);
+
+ clearAssociationForPackage(userId, packageName);
}
@Override
public void onPackageModified(String packageName) {
- Slog.d(LOG_TAG, "onPackageModified(packageName = " + packageName + ")");
- int userId = getChangingUserId();
- forEach(getAllAssociations(userId, packageName), association -> {
- updateSpecialAccessPermissionForAssociatedPackage(association);
- });
- }
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageModified() u" + userId + "/" + packageName);
+ forEach(getAssociations(userId, packageName), association ->
+ updateSpecialAccessPermissionForAssociatedPackage(association));
+ }
}.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
}
@@ -269,18 +276,82 @@
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
- int userHandle = user.getUserIdentifier();
- Set<AssociationInfo> associations = getAllAssociations(userHandle);
- if (associations == null || associations.isEmpty()) {
- return;
- }
- updateAtm(userHandle, associations);
+ final int userId = user.getUserIdentifier();
+ final Set<AssociationInfo> associations = getAllAssociationsForUser(userId);
+
+ if (associations.isEmpty()) return;
+
+ updateAtm(userId, associations);
BackgroundThread.getHandler().sendMessageDelayed(
obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
MINUTES.toMillis(10));
}
+ @NonNull
+ Set<AssociationInfo> getAllAssociationsForUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ readPersistedStateForUserIfNeededLocked(userId);
+ // This returns non-null, because the readAssociationsInfoForUserIfNeededLocked() method
+ // we just called adds an empty set, if there was no previously saved data.
+ return mCachedAssociations.get(userId);
+ }
+ }
+
+ @NonNull
+ Set<AssociationInfo> getAssociations(@UserIdInt int userId, @NonNull String packageName) {
+ return filter(getAllAssociationsForUser(userId),
+ a -> a.belongsToPackage(userId, packageName));
+ }
+
+ @Nullable
+ private AssociationInfo getAssociation(int associationId) {
+ return find(getAllAssociations(), association -> association.getId() == associationId);
+ }
+
+ @Nullable
+ AssociationInfo getAssociation(
+ @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
+ return find(getAssociations(userId, packageName), a -> a.isLinkedTo(macAddress));
+ }
+
+ @Nullable
+ AssociationInfo getAssociationWithCallerChecks(
+ @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
+ return sanitizeWithCallerChecks(getAssociation(userId, packageName, macAddress));
+ }
+
+ @Nullable
+ AssociationInfo getAssociationWithCallerChecks(int associationId) {
+ return sanitizeWithCallerChecks(getAssociation(associationId));
+ }
+
+ @Nullable
+ private AssociationInfo sanitizeWithCallerChecks(@Nullable AssociationInfo association) {
+ if (association == null) return null;
+
+ final int userId = association.getUserId();
+ final String packageName = association.getPackageName();
+ if (!checkCallerCanManageAssociationsForPackage(getContext(), userId, packageName)) {
+ return null;
+ }
+
+ return association;
+ }
+
+ private Set<AssociationInfo> getAllAssociations() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Set<AssociationInfo> result = new ArraySet<>();
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ result.addAll(getAllAssociationsForUser(user.id));
+ }
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
void maybeGrantAutoRevokeExemptions() {
Slog.d(LOG_TAG, "maybeGrantAutoRevokeExemptions()");
PackageManager pm = getContext().getPackageManager();
@@ -293,7 +364,7 @@
}
try {
- Set<AssociationInfo> associations = getAllAssociations(userId);
+ Set<AssociationInfo> associations = getAllAssociationsForUser(userId);
if (associations == null) {
continue;
}
@@ -325,67 +396,92 @@
}
@Override
- public void associate(
- AssociationRequest request,
- IFindDeviceCallback callback,
- String callingPackage) throws RemoteException {
- Slog.i(LOG_TAG, "associate(request = " + request + ", callback = " + callback
- + ", callingPackage = " + callingPackage + ")");
- mAssociationRequestsProcessor.process(request, callback, callingPackage);
+ public void associate(AssociationRequest request, IAssociationRequestCallback callback,
+ String packageName, int userId) throws RemoteException {
+ Slog.i(LOG_TAG, "associate() "
+ + "request=" + request + ", "
+ + "package=u" + userId + "/" + packageName);
+ mAssociationRequestsProcessor.process(request, packageName, userId, callback);
}
@Override
- public void stopScan(AssociationRequest request,
- IFindDeviceCallback callback,
- String callingPackage) {
- Slog.i(LOG_TAG, "stopScan(request = " + request + ")");
- mAssociationRequestsProcessor.stopScan(request, callback, callingPackage);
- }
-
- @Override
- public List<String> getAssociations(String callingPackage, int userId)
- throws RemoteException {
- if (!callerCanManageCompanionDevices()) {
- checkCallerIsSystemOr(callingPackage, userId);
- checkUsesFeature(callingPackage, getCallingUserId());
- }
- return new ArrayList<>(map(
- getAllAssociations(userId, callingPackage),
- a -> a.getDeviceMacAddress()));
- }
-
- @Override
- public List<AssociationInfo> getAssociationsForUser(int userId) {
- if (!callerCanManageCompanionDevices()) {
- throw new SecurityException("Caller must hold "
- + android.Manifest.permission.MANAGE_COMPANION_DEVICES);
+ public List<AssociationInfo> getAssociations(String packageName, int userId) {
+ final int callingUid = getCallingUserId();
+ if (!checkCallerCanManageAssociationsForPackage(getContext(), userId, packageName)) {
+ throw new SecurityException("Caller (uid=" + callingUid + ") does not have "
+ + "permissions to get associations for u" + userId + "/" + packageName);
}
- return new ArrayList<>(getAllAssociations(userId, null /* packageFilter */));
- }
+ if (!checkCallerCanManageCompanionDevice(getContext())) {
+ // If the caller neither is system nor holds MANAGE_COMPANION_DEVICES: it needs to
+ // request the feature (also: the caller is the app itself).
+ checkUsesFeature(packageName, getCallingUserId());
+ }
- //TODO also revoke notification access
- @Override
- public void disassociate(String deviceMacAddress, String callingPackage)
- throws RemoteException {
- checkNotNull(deviceMacAddress);
- checkCallerIsSystemOr(callingPackage);
- checkUsesFeature(callingPackage, getCallingUserId());
- removeAssociation(getCallingUserId(), callingPackage, deviceMacAddress);
- }
-
- private boolean callerCanManageCompanionDevices() {
- return getContext().checkCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_COMPANION_DEVICES)
- == PERMISSION_GRANTED;
+ return new ArrayList<>(
+ CompanionDeviceManagerService.this.getAssociations(userId, packageName));
}
@Override
- public PendingIntent requestNotificationAccess(ComponentName component)
+ public List<AssociationInfo> getAllAssociationsForUser(int userId) throws RemoteException {
+ enforceCallerCanInteractWithUserId(getContext(), userId);
+ enforceCallerCanManagerCompanionDevice(getContext(), "getAllAssociationsForUser");
+
+ return new ArrayList<>(
+ CompanionDeviceManagerService.this.getAllAssociationsForUser(userId));
+ }
+
+ @Override
+ public void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener,
+ int userId) {
+ enforceCallerCanInteractWithUserId(getContext(), userId);
+ enforceCallerCanManagerCompanionDevice(getContext(),
+ "addOnAssociationsChangedListener");
+
+ //TODO: Implement.
+ }
+
+ @Override
+ public void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener,
+ int userId) {
+ //TODO: Implement.
+ }
+
+ @Override
+ public void legacyDisassociate(String deviceMacAddress, String packageName, int userId) {
+ requireNonNull(deviceMacAddress);
+ requireNonNull(packageName);
+
+ final AssociationInfo association =
+ getAssociationWithCallerChecks(userId, packageName, deviceMacAddress);
+ if (association == null) {
+ throw new IllegalArgumentException("Association does not exist "
+ + "or the caller does not have permissions to manage it "
+ + "(ie. it belongs to a different package or a different user).");
+ }
+
+ disassociateInternal(userId, association.getId());
+ }
+
+ @Override
+ public void disassociate(int associationId) {
+ final AssociationInfo association = getAssociationWithCallerChecks(associationId);
+ if (association == null) {
+ throw new IllegalArgumentException("Association with ID " + associationId + " "
+ + "does not exist "
+ + "or belongs to a different package "
+ + "or belongs to a different user");
+ }
+
+ disassociateInternal(association.getUserId(), associationId);
+ }
+
+ @Override
+ public PendingIntent requestNotificationAccess(ComponentName component, int userId)
throws RemoteException {
String callingPackage = component.getPackageName();
checkCanCallNotificationApi(callingPackage);
- int userId = getCallingUserId();
+ //TODO: check userId.
String packageTitle = BidiFormatter.getInstance().unicodeWrap(
getPackageInfo(callingPackage, userId)
.applicationInfo
@@ -425,7 +521,7 @@
public boolean isDeviceAssociatedForWifiConnection(String packageName, String macAddress,
int userId) {
getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_COMPANION_DEVICES, "isDeviceAssociated");
+ MANAGE_COMPANION_DEVICES, "isDeviceAssociated");
boolean bypassMacPermission = getContext().getPackageManager().checkPermission(
android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS, packageName)
@@ -434,23 +530,22 @@
return true;
}
- return any(
- getAllAssociations(userId, packageName),
- a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
+ return any(CompanionDeviceManagerService.this.getAssociations(userId, packageName),
+ a -> a.isLinkedTo(macAddress));
}
@Override
- public void registerDevicePresenceListenerService(
- String packageName, String deviceAddress)
- throws RemoteException {
- registerDevicePresenceListenerActive(packageName, deviceAddress, true);
+ public void registerDevicePresenceListenerService(String deviceAddress,
+ String callingPackage, int userId) throws RemoteException {
+ //TODO: take the userId into account.
+ registerDevicePresenceListenerActive(callingPackage, deviceAddress, true);
}
@Override
- public void unregisterDevicePresenceListenerService(
- String packageName, String deviceAddress)
- throws RemoteException {
- registerDevicePresenceListenerActive(packageName, deviceAddress, false);
+ public void unregisterDevicePresenceListenerService(String deviceAddress,
+ String callingPackage, int userId) throws RemoteException {
+ //TODO: take the userId into account.
+ registerDevicePresenceListenerActive(callingPackage, deviceAddress, false);
}
@Override
@@ -464,12 +559,12 @@
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE,
"[un]registerDevicePresenceListenerService");
- checkCallerIsSystemOr(packageName);
+ final int userId = getCallingUserId();
+ enforceCallerIsSystemOr(userId, packageName);
- int userId = getCallingUserId();
Set<AssociationInfo> deviceAssociations = filter(
- getAllAssociations(userId, packageName),
- association -> deviceAddress.equals(association.getDeviceMacAddress()));
+ CompanionDeviceManagerService.this.getAssociations(userId, packageName),
+ a -> a.isLinkedTo(deviceAddress));
if (deviceAssociations.isEmpty()) {
throw new RemoteException(new DeviceNotAssociatedException("App " + packageName
@@ -478,8 +573,8 @@
}
updateAssociations(associations -> map(associations, association -> {
- if (Objects.equals(association.getPackageName(), packageName)
- && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+ if (association.belongsToPackage(userId, packageName)
+ && association.isLinkedTo(deviceAddress)) {
association.setNotifyOnDeviceNearby(active);
}
return association;
@@ -500,32 +595,34 @@
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES, "createAssociation");
- createAssociationInternal(userId, macAddress, packageName, null);
+ legacyCreateAssociation(userId, macAddress, packageName, null);
}
- private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
- checkCallerIsSystemOr(callingPackage);
- int userId = getCallingUserId();
- checkState(!ArrayUtils.isEmpty(getAllAssociations(userId, callingPackage)),
+ private void checkCanCallNotificationApi(String callingPackage) {
+ final int userId = getCallingUserId();
+ enforceCallerIsSystemOr(userId, callingPackage);
+
+ checkState(!ArrayUtils.isEmpty(
+ CompanionDeviceManagerService.this.getAssociations(userId, callingPackage)),
"App must have an association before calling this API");
checkUsesFeature(callingPackage, userId);
}
@Override
- public boolean canPairWithoutPrompt(
- String packageName, String deviceMacAddress, int userId) {
- return any(
- getAllAssociations(userId, packageName, deviceMacAddress),
- a -> System.currentTimeMillis() - a.getTimeApprovedMs()
- < PAIR_WITHOUT_PROMPT_WINDOW_MS);
+ public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) {
+ final AssociationInfo association = getAssociation(userId, packageName, macAddress);
+ if (association == null) {
+ return false;
+ }
+ return System.currentTimeMillis() - association.getTimeApprovedMs()
+ < PAIR_WITHOUT_PROMPT_WINDOW_MS;
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
throws RemoteException {
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_COMPANION_DEVICES, null);
+ enforceCallerCanManagerCompanionDevice(getContext(), "onShellCommand");
new CompanionDeviceShellCommand(CompanionDeviceManagerService.this)
.exec(this, in, out, err, args, callback, resultReceiver);
}
@@ -575,62 +672,29 @@
}
}
- void checkCallerIsSystemOr(String pkg) throws RemoteException {
- checkCallerIsSystemOr(pkg, getCallingUserId());
+ /**
+ * @deprecated use
+ * {@link #createAssociation(int, String, MacAddress, CharSequence, String, boolean)}
+ */
+ @Deprecated
+ void legacyCreateAssociation(@UserIdInt int userId, @NonNull String deviceMacAddress,
+ @NonNull String packageName, @Nullable String deviceProfile) {
+ final MacAddress macAddress = MacAddress.fromString(deviceMacAddress);
+ createAssociation(userId, packageName, macAddress, null, deviceProfile, false);
}
- private void checkCallerIsSystemOr(String pkg, int userId) throws RemoteException {
- if (isCallerSystem()) {
- return;
- }
-
- checkArgument(getCallingUserId() == userId,
- "Must be called by either same user or system");
- int callingUid = Binder.getCallingUid();
- if (mAppOpsManager.checkPackage(callingUid, pkg) != AppOpsManager.MODE_ALLOWED) {
- throw new SecurityException(pkg + " doesn't belong to uid " + callingUid);
- }
- }
-
- static int getCallingUserId() {
- return UserHandle.getUserId(Binder.getCallingUid());
- }
-
- private static boolean isCallerSystem() {
- return Binder.getCallingUid() == Process.SYSTEM_UID;
- }
-
- void checkUsesFeature(String pkg, int userId) {
- if (isCallerSystem()) {
- // Drop the requirement for calls from system process
- return;
- }
-
- FeatureInfo[] reqFeatures = getPackageInfo(pkg, userId).reqFeatures;
- String requiredFeature = PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
- int numFeatures = ArrayUtils.size(reqFeatures);
- for (int i = 0; i < numFeatures; i++) {
- if (requiredFeature.equals(reqFeatures[i].name)) return;
- }
- throw new IllegalStateException("Must declare uses-feature "
- + requiredFeature
- + " in manifest to use this API");
- }
-
- void createAssociationInternal(
- int userId, String deviceMacAddress, String packageName, String deviceProfile) {
- final AssociationInfo association = new AssociationInfo(
- getNewAssociationIdForPackage(userId, packageName),
- userId,
- packageName,
- Arrays.asList(new DeviceId(TYPE_MAC_ADDRESS, deviceMacAddress)),
- deviceProfile,
- /* managedByCompanionApp */false,
- /* notifyOnDeviceNearby */ false ,
- System.currentTimeMillis());
+ AssociationInfo createAssociation(@UserIdInt int userId, @NonNull String packageName,
+ @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
+ @Nullable String deviceProfile, boolean selfManaged) {
+ final int id = getNewAssociationIdForPackage(userId, packageName);
+ final long timestamp = System.currentTimeMillis();
+ final AssociationInfo association = new AssociationInfo(id, userId, packageName,
+ macAddress, displayName, deviceProfile, selfManaged, false, timestamp);
updateSpecialAccessPermissionForAssociatedPackage(association);
recordAssociation(association, userId);
+
+ return association;
}
@GuardedBy("mLock")
@@ -648,8 +712,8 @@
// First: collect all IDs currently in use for this user's Associations.
final SparseBooleanArray usedIds = new SparseBooleanArray();
- for (AssociationInfo it : getAllAssociations(userId)) {
- usedIds.put(it.getAssociationId(), true);
+ for (AssociationInfo it : getAllAssociationsForUser(userId)) {
+ usedIds.put(it.getId(), true);
}
// Second: collect all IDs that have been previously used for this package (and user).
@@ -674,19 +738,29 @@
}
}
- void removeAssociation(int userId, String packageName, String deviceMacAddress) {
- updateAssociations(associations -> filterOut(associations, it -> {
- final boolean match = it.belongsToPackage(userId, packageName)
- && Objects.equals(it.getDeviceMacAddress(), deviceMacAddress);
- if (match) {
- onAssociationPreRemove(it);
- markIdAsPreviouslyUsedForPackage(it.getAssociationId(), userId, packageName);
- }
- return match;
- }), userId);
+ //TODO also revoke notification access
+ void disassociateInternal(@UserIdInt int userId, int associationId) {
+ updateAssociations(associations ->
+ filterOut(associations, it -> {
+ if (it.getId() != associationId) return false;
+
+ onAssociationPreRemove(it);
+ markIdAsPreviouslyUsedForPackage(
+ it.getId(), it.getUserId(), it.getPackageName());
+ return true;
+ }), userId);
+
restartBleScan();
}
+ void clearAssociationForPackage(@UserIdInt int userId, @NonNull String packageName) {
+ if (DEBUG) Slog.d(LOG_TAG, "clearAssociationForPackage() u" + userId + "/" + packageName);
+
+ mCompanionDevicePresenceController.unbindDevicePresenceListener(packageName, userId);
+ updateAssociations(set -> filterOut(set, it -> it.belongsToPackage(userId, packageName)),
+ userId);
+ }
+
private void markIdAsPreviouslyUsedForPackage(
int associationId, @UserIdInt int userId, @NonNull String packageName) {
synchronized (mLock) {
@@ -707,32 +781,15 @@
String deviceProfile = association.getDeviceProfile();
if (deviceProfile != null) {
AssociationInfo otherAssociationWithDeviceProfile = find(
- getAllAssociations(association.getUserId()),
+ getAllAssociationsForUser(association.getUserId()),
a -> !a.equals(association) && deviceProfile.equals(a.getDeviceProfile()));
if (otherAssociationWithDeviceProfile != null) {
Slog.i(LOG_TAG, "Not revoking " + deviceProfile
+ " for " + association
+ " - profile still present in " + otherAssociationWithDeviceProfile);
} else {
- final long identity = Binder.clearCallingIdentity();
- try {
- mRoleManager.removeRoleHolderAsUser(
- association.getDeviceProfile(),
- association.getPackageName(),
- RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP,
- UserHandle.of(association.getUserId()),
- getContext().getMainExecutor(),
- success -> {
- if (!success) {
- Slog.e(LOG_TAG, "Failed to revoke device profile role "
- + association.getDeviceProfile()
- + " to " + association.getPackageName()
- + " for user " + association.getUserId());
- }
- });
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ Binder.withCleanCallingIdentity(
+ () -> removeRoleHolderForAssociation(getContext(), association));
}
}
}
@@ -780,9 +837,9 @@
exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
- if (!association.isManagedByCompanionApp()) {
- if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
- grantDeviceProfile(association);
+ if (!association.isSelfManaged()) {
+ if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddressAsString())) {
+ addRoleHolderForAssociation(getContext(), association);
}
if (association.isNotifyOnDeviceNearby()) {
@@ -810,17 +867,10 @@
@Nullable
private PackageInfo getPackageInfo(String packageName, int userId) {
- return Binder.withCleanCallingIdentity(PooledLambda.obtainSupplier((context, pkg, id) -> {
- try {
- return context.getPackageManager().getPackageInfoAsUser(
- pkg,
- PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS,
- id);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + pkg, e);
- return null;
- }
- }, getContext(), packageName, userId).recycleOnUse());
+ final int flags = PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS;
+ return Binder.withCleanCallingIdentity(
+ () -> getContext().getPackageManager()
+ .getPackageInfoAsUser(packageName, flags , userId));
}
private void recordAssociation(AssociationInfo association, int userId) {
@@ -833,7 +883,7 @@
synchronized (mLock) {
if (DEBUG) Slog.d(LOG_TAG, "Updating Associations set...");
- final Set<AssociationInfo> prevAssociations = getAllAssociations(userId);
+ final Set<AssociationInfo> prevAssociations = getAllAssociationsForUser(userId);
if (DEBUG) Slog.d(LOG_TAG, " > Before : " + prevAssociations + "...");
final Set<AssociationInfo> updatedAssociations = update.apply(
@@ -871,15 +921,6 @@
}
}
- @NonNull Set<AssociationInfo> getAllAssociations(int userId) {
- synchronized (mLock) {
- readPersistedStateForUserIfNeededLocked(userId);
- // This returns non-null, because the readAssociationsInfoForUserIfNeededLocked() method
- // we just called adds an empty set, if there was no previously saved data.
- return mCachedAssociations.get(userId);
- }
- }
-
@GuardedBy("mLock")
private void readPersistedStateForUserIfNeededLocked(@UserIdInt int userId) {
if (mCachedAssociations.get(userId) != null) return;
@@ -908,49 +949,20 @@
}
}
- Set<AssociationInfo> getAllAssociations(int userId, @Nullable String packageFilter) {
- return filter(
- getAllAssociations(userId),
- // Null filter == get all associations
- a -> packageFilter == null || Objects.equals(packageFilter, a.getPackageName()));
- }
-
- private Set<AssociationInfo> getAllAssociations() {
- final long identity = Binder.clearCallingIdentity();
- try {
- ArraySet<AssociationInfo> result = new ArraySet<>();
- for (UserInfo user : mUserManager.getAliveUsers()) {
- result.addAll(getAllAssociations(user.id));
- }
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private Set<AssociationInfo> getAllAssociations(
- int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
- return filter(
- getAllAssociations(userId),
- // Null filter == get all associations
- a -> (packageFilter == null || Objects.equals(packageFilter, a.getPackageName()))
- && (addressFilter == null
- || Objects.equals(addressFilter, a.getDeviceMacAddress())));
- }
-
void onDeviceConnected(String address) {
Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
mCurrentlyConnectedDevices.add(address);
for (UserInfo user : getAllUsers()) {
- for (AssociationInfo association : getAllAssociations(user.id)) {
- if (Objects.equals(address, association.getDeviceMacAddress())) {
+ for (AssociationInfo association : getAllAssociationsForUser(user.id)) {
+ if (association.isLinkedTo(address)) {
if (association.getDeviceProfile() != null) {
Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
+ " to " + association.getPackageName()
+ " due to device connected: " + association.getDeviceMacAddress());
- grantDeviceProfile(association);
+
+ addRoleHolderForAssociation(getContext(), association);
}
}
}
@@ -959,27 +971,6 @@
onDeviceNearby(address);
}
- private void grantDeviceProfile(AssociationInfo association) {
- Slog.i(LOG_TAG, "grantDeviceProfile(association = " + association + ")");
-
- if (association.getDeviceProfile() != null) {
- mRoleManager.addRoleHolderAsUser(
- association.getDeviceProfile(),
- association.getPackageName(),
- RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP,
- UserHandle.of(association.getUserId()),
- getContext().getMainExecutor(),
- success -> {
- if (!success) {
- Slog.e(LOG_TAG, "Failed to grant device profile role "
- + association.getDeviceProfile()
- + " to " + association.getPackageName()
- + " for user " + association.getUserId());
- }
- });
- }
- }
-
void onDeviceDisconnected(String address) {
Slog.d(LOG_TAG, "onDeviceDisconnected(address = " + address + ")");
@@ -1113,8 +1104,8 @@
Set<AssociationInfo> result = new ArraySet<>();
for (int i = 0, size = aliveUsers.size(); i < size; i++) {
UserInfo user = aliveUsers.get(i);
- for (AssociationInfo association : getAllAssociations(user.id)) {
- if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+ for (AssociationInfo association : getAllAssociationsForUser(user.id)) {
+ if (association.isLinkedTo(deviceAddress)) {
result.add(association);
}
}
@@ -1224,10 +1215,10 @@
ArrayList<ScanFilter> result = new ArrayList<>();
ArraySet<String> addressesSeen = new ArraySet<>();
for (AssociationInfo association : getAllAssociations()) {
- if (association.isManagedByCompanionApp()) {
+ if (association.isSelfManaged()) {
continue;
}
- String address = association.getDeviceMacAddress();
+ String address = association.getDeviceMacAddressAsString();
if (addressesSeen.contains(address)) {
continue;
}
@@ -1273,4 +1264,19 @@
forEach(orig, (key, value) -> copy.put(key, new ArraySet<>(value)));
return copy;
}
+
+ void checkUsesFeature(@NonNull String pkg, @UserIdInt int userId) {
+ if (getCallingUid() == SYSTEM_UID) return;
+
+ final FeatureInfo[] requestedFeatures = getPackageInfo(pkg, userId).reqFeatures;
+ if (requestedFeatures != null) {
+ for (int i = 0; i < requestedFeatures.length; i++) {
+ if (FEATURE_COMPANION_DEVICE_SETUP.equals(requestedFeatures[i].name)) return;
+ }
+ }
+
+ throw new IllegalStateException("Must declare uses-feature "
+ + FEATURE_COMPANION_DEVICE_SETUP
+ + " in manifest to use this API");
+ }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
index a79db2c..3e00846 100644
--- a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
+++ b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
@@ -67,7 +67,7 @@
Slog.i(LOG_TAG,
"Sending onDeviceAppeared to " + association.getPackageName() + ")");
primaryConnector.run(
- service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
+ s -> s.onDeviceAppeared(association.getDeviceMacAddressAsString()));
}
}
@@ -78,7 +78,7 @@
Slog.i(LOG_TAG,
"Sending onDeviceDisappeared to " + association.getPackageName() + ")");
primaryConnector.run(
- service -> service.onDeviceDisappeared(association.getDeviceMacAddress()));
+ s -> s.onDeviceDisappeared(association.getDeviceMacAddressAsString()));
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index e143f5e..5cb3079 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -19,6 +19,7 @@
import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+import android.companion.AssociationInfo;
import android.util.Log;
import android.util.Slog;
@@ -37,7 +38,7 @@
switch (cmd) {
case "list": {
forEach(
- mService.getAllAssociations(getNextArgInt()),
+ mService.getAllAssociationsForUser(getNextArgInt()),
a -> getOutPrintWriter()
.println(a.getPackageName() + " "
+ a.getDeviceMacAddress()));
@@ -48,13 +49,19 @@
int userId = getNextArgInt();
String packageName = getNextArgRequired();
String address = getNextArgRequired();
- mService.createAssociationInternal(userId, address, packageName, null);
+ mService.legacyCreateAssociation(userId, address, packageName, null);
}
break;
case "disassociate": {
- mService.removeAssociation(getNextArgInt(), getNextArgRequired(),
- getNextArgRequired());
+ final int userId = getNextArgInt();
+ final String packageName = getNextArgRequired();
+ final String address = getNextArgRequired();
+ final AssociationInfo association =
+ mService.getAssociationWithCallerChecks(userId, packageName, address);
+ if (association != null) {
+ mService.disassociateInternal(userId, association.getId());
+ }
}
break;
diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/PermissionsUtils.java
new file mode 100644
index 0000000..0d38fa3
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/PermissionsUtils.java
@@ -0,0 +1,203 @@
+/*
+ * 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.server.companion;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
+import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Binder.getCallingUid;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.UserHandle.getCallingUserId;
+
+import static java.util.Collections.unmodifiableMap;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.AssociationRequest;
+import android.companion.CompanionDeviceManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+
+import com.android.internal.app.IAppOpsService;
+
+import java.util.Map;
+
+/**
+ * Utility methods for checking permissions required for accessing {@link CompanionDeviceManager}
+ * APIs (such as {@link Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH},
+ * {@link Manifest.permission#REQUEST_COMPANION_PROFILE_APP_STREAMING},
+ * {@link Manifest.permission#REQUEST_COMPANION_SELF_MANAGED} etc.)
+ */
+final class PermissionsUtils {
+
+ private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION;
+ static {
+ final Map<String, String> map = new ArrayMap<>();
+ map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
+ map.put(DEVICE_PROFILE_APP_STREAMING,
+ Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING);
+ map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
+ Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION);
+
+ DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
+ }
+
+ static void enforceCallerPermissionsToRequest(@NonNull Context context,
+ @NonNull AssociationRequest request, @NonNull String packageName,
+ @UserIdInt int userId) {
+ enforceCallerCanInteractWithUserId(context, userId);
+ enforceCallerIsSystemOr(userId, packageName);
+
+ enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile());
+
+ if (request.isSelfManaged()) {
+ enforceRequestSelfManagedPermission(context);
+ }
+ }
+
+ static void enforceRequestDeviceProfilePermissions(
+ @NonNull Context context, @Nullable String deviceProfile) {
+ // Device profile can be null.
+ if (deviceProfile == null) return;
+
+ if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
+ throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
+ }
+
+ if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) {
+ // TODO: remove, when properly supporting this profile.
+ throw new UnsupportedOperationException(
+ "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
+ }
+
+ if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) {
+ // TODO: remove, when properly supporting this profile.
+ throw new UnsupportedOperationException(
+ "DEVICE_PROFILE_AUTOMOTIVE_PROJECTION is not fully supported yet.");
+ }
+
+ final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
+ if (context.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
+ throw new SecurityException("Application must hold " + permission + " to associate "
+ + "with a device with " + deviceProfile + " profile.");
+ }
+ }
+
+ static void enforceRequestSelfManagedPermission(@NonNull Context context) {
+ if (context.checkCallingOrSelfPermission(REQUEST_COMPANION_SELF_MANAGED)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Application does not hold "
+ + REQUEST_COMPANION_SELF_MANAGED);
+ }
+ }
+
+ static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) {
+ if (getCallingUserId() == userId) return true;
+
+ return context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED;
+ }
+
+ static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) {
+ if (getCallingUserId() == userId) return;
+
+ context.enforceCallingPermission(INTERACT_ACROSS_USERS, null);
+ }
+
+ static boolean checkCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) {
+ final int callingUid = getCallingUid();
+ if (callingUid == SYSTEM_UID) return true;
+
+ if (getCallingUserId() != userId) return false;
+
+ if (!checkPackage(callingUid, packageName)) return false;
+
+ return true;
+ }
+
+ static void enforceCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) {
+ final int callingUid = getCallingUid();
+ if (callingUid == SYSTEM_UID) return;
+
+ final int callingUserId = getCallingUserId();
+ if (getCallingUserId() != userId) {
+ throw new SecurityException("Calling UserId (" + callingUserId + ") does not match "
+ + "the expected UserId (" + userId + ")");
+ }
+
+ if (!checkPackage(callingUid, packageName)) {
+ throw new SecurityException(packageName + " doesn't belong to calling uid ("
+ + callingUid + ")");
+ }
+ }
+
+ static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
+ if (getCallingUserId() == SYSTEM_UID) return true;
+
+ return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
+ }
+
+ static void enforceCallerCanManagerCompanionDevice(@NonNull Context context,
+ @Nullable String message) {
+ if (getCallingUserId() == SYSTEM_UID) return;
+
+ context.enforceCallingPermission(MANAGE_COMPANION_DEVICES, message);
+ }
+
+ static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
+ @UserIdInt int userId, @NonNull String packageName) {
+ if (checkCallerIsSystemOr(userId, packageName)) return true;
+
+ if (!checkCallerCanInteractWithUserId(context, userId)) return false;
+
+ return checkCallerCanManageCompanionDevice(context);
+ }
+
+ private static boolean checkPackage(@UserIdInt int uid, @NonNull String packageName) {
+ try {
+ return getAppOpsService().checkPackage(uid, packageName) == MODE_ALLOWED;
+ } catch (RemoteException e) {
+ // Can't happen: AppOpsManager is running in the same process.
+ return true;
+ }
+ }
+
+ private static IAppOpsService getAppOpsService() {
+ if (sAppOpsService == null) {
+ synchronized (PermissionsUtils.class) {
+ if (sAppOpsService == null) {
+ sAppOpsService = IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE));
+ }
+ }
+ }
+ return sAppOpsService;
+ }
+
+ // DO NOT USE DIRECTLY! Access via getAppOpsService().
+ private static IAppOpsService sAppOpsService = null;
+
+ private PermissionsUtils() {}
+}
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 5b8d7e5..87558df 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -16,8 +16,6 @@
package com.android.server.companion;
-import static android.companion.DeviceId.TYPE_MAC_ADDRESS;
-
import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -35,7 +33,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
-import android.companion.DeviceId;
+import android.net.MacAddress;
import android.os.Environment;
import android.util.AtomicFile;
import android.util.ExceptionUtils;
@@ -53,10 +51,7 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -101,8 +96,8 @@
* Since Android T the data is stored using the v1 schema.
* In the v1 schema, a list of the previously used IDs is storead along with the association
* records.
- * In the v1 schema, we no longer store MAC addresses, instead each assocition record may have a
- * number of DeviceIds.
+ * V1 schema adds a new optional `display_name` attribute, and makes the `mac_address` attribute
+ * optional.
*
* @see #CURRENT_PERSISTENCE_VERSION
* @see #readAssociationsV1(TypedXmlPullParser, int, Set)
@@ -116,21 +111,19 @@
* <association
* id="1"
* package="com.sample.companion.app"
- * managed_by_app="false"
+ * mac_address="AA:BB:CC:DD:EE:00"
+ * self_managed="false"
* notify_device_nearby="false"
- * time_approved="1634389553216">
- * <device-id type="mac_address" value="AA:BB:CC:DD:EE:00" />
- * </association>
+ * time_approved="1634389553216"/>
*
* <association
* id="3"
* profile="android.app.role.COMPANION_DEVICE_WATCH"
* package="com.sample.companion.another.app"
- * managed_by_app="false"
+ * display_name="Jhon's Chromebook"
+ * self_managed="true"
* notify_device_nearby="false"
- * time_approved="1634641160229">
- * <device-id type="mac_address" value="AA:BB:CC:DD:EE:FF" />
- * </association>
+ * time_approved="1634641160229"/>
* </associations>
*
* <previously-used-ids>
@@ -153,7 +146,6 @@
private static final String XML_TAG_STATE = "state";
private static final String XML_TAG_ASSOCIATIONS = "associations";
private static final String XML_TAG_ASSOCIATION = "association";
- private static final String XML_TAG_DEVICE_ID = "device-id";
private static final String XML_TAG_PREVIOUSLY_USED_IDS = "previously-used-ids";
private static final String XML_TAG_PACKAGE = "package";
private static final String XML_TAG_ID = "id";
@@ -164,13 +156,14 @@
private static final String XML_ATTR_PACKAGE_NAME = "package_name";
// Used in <association> elements, nested within <associations> elements.
private static final String XML_ATTR_PACKAGE = "package";
- private static final String XML_ATTR_DEVICE = "device";
+ private static final String XML_ATTR_MAC_ADDRESS = "mac_address";
+ private static final String XML_ATTR_DISPLAY_NAME = "display_name";
private static final String XML_ATTR_PROFILE = "profile";
- private static final String XML_ATTR_MANAGED_BY_APP = "managed_by_app";
+ private static final String XML_ATTR_SELF_MANAGED = "self_managed";
private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
private static final String XML_ATTR_TIME_APPROVED = "time_approved";
- private static final String XML_ATTR_TYPE = "type";
- private static final String XML_ATTR_VALUE = "value";
+
+ private static final String LEGACY_XML_ATTR_DEVICE = "device";
private final @NonNull ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
new ConcurrentHashMap<>();
@@ -353,9 +346,7 @@
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
- // In v0, CDM did not have a notion of a DeviceId yet, instead each Association had a MAC
- // address.
- final String deviceAddress = readStringAttribute(parser, XML_ATTR_DEVICE);
+ final String deviceAddress = readStringAttribute(parser, LEGACY_XML_ATTR_DEVICE);
if (appPackage == null || deviceAddress == null) return;
@@ -363,10 +354,8 @@
final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
- // "Convert" MAC address into a DeviceId.
- final List<DeviceId> deviceIds = Arrays.asList(
- new DeviceId(TYPE_MAC_ADDRESS, deviceAddress));
- out.add(new AssociationInfo(associationId, userId, appPackage, deviceIds, profile,
+ out.add(new AssociationInfo(associationId, userId, appPackage,
+ MacAddress.fromString(deviceAddress), null, profile,
/* managedByCompanionApp */false, notify, timeApproved));
}
@@ -391,23 +380,18 @@
final int associationId = readIntAttribute(parser, XML_ATTR_ID);
final String profile = readStringAttribute(parser, XML_ATTR_PROFILE);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
- final boolean managedByApp = readBooleanAttribute(parser, XML_ATTR_MANAGED_BY_APP);
+ final MacAddress macAddress = stringToMacAddress(
+ readStringAttribute(parser, XML_ATTR_MAC_ADDRESS));
+ final String displayName = readStringAttribute(parser, XML_ATTR_DISPLAY_NAME);
+ final boolean selfManaged = readBooleanAttribute(parser, XML_ATTR_SELF_MANAGED);
final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
- final List<DeviceId> deviceIds = new ArrayList<>();
- while (true) {
- parser.nextTag();
- if (isEndOfTag(parser, XML_TAG_ASSOCIATION)) break;
- if (!isStartOfTag(parser, XML_TAG_DEVICE_ID)) continue;
-
- final String type = readStringAttribute(parser, XML_ATTR_TYPE);
- final String value = readStringAttribute(parser, XML_ATTR_VALUE);
- deviceIds.add(new DeviceId(type, value));
+ final AssociationInfo associationInfo = createAssociationInfoNoThrow(associationId, userId,
+ appPackage, macAddress, displayName, profile, selfManaged, notify, timeApproved);
+ if (associationInfo != null) {
+ out.add(associationInfo);
}
-
- out.add(new AssociationInfo(associationId, userId, appPackage, deviceIds, profile,
- managedByApp, notify, timeApproved));
}
private static void readPreviouslyUsedIdsV1(@NonNull TypedXmlPullParser parser,
@@ -447,32 +431,19 @@
throws IOException {
final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATION);
- writeIntAttribute(serializer, XML_ATTR_ID, a.getAssociationId());
+ writeIntAttribute(serializer, XML_ATTR_ID, a.getId());
writeStringAttribute(serializer, XML_ATTR_PROFILE, a.getDeviceProfile());
writeStringAttribute(serializer, XML_ATTR_PACKAGE, a.getPackageName());
- writeBooleanAttribute(serializer, XML_ATTR_MANAGED_BY_APP, a.isManagedByCompanionApp());
+ writeStringAttribute(serializer, XML_ATTR_MAC_ADDRESS, a.getDeviceMacAddressAsString());
+ writeStringAttribute(serializer, XML_ATTR_DISPLAY_NAME, a.getDisplayName());
+ writeBooleanAttribute(serializer, XML_ATTR_SELF_MANAGED, a.isSelfManaged());
writeBooleanAttribute(
serializer, XML_ATTR_NOTIFY_DEVICE_NEARBY, a.isNotifyOnDeviceNearby());
writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs());
- final List<DeviceId> deviceIds = a.getDeviceIds();
- for (int i = 0, size = deviceIds.size(); i < size; i++) {
- writeDeviceId(serializer, deviceIds.get(i));
- }
-
serializer.endTag(null, XML_TAG_ASSOCIATION);
}
- private static void writeDeviceId(@NonNull XmlSerializer parent, @NonNull DeviceId deviceId)
- throws IOException {
- final XmlSerializer serializer = parent.startTag(null, XML_TAG_DEVICE_ID);
-
- writeStringAttribute(serializer, XML_ATTR_TYPE, deviceId.getType());
- writeStringAttribute(serializer, XML_ATTR_VALUE, deviceId.getValue());
-
- serializer.endTag(null, XML_TAG_DEVICE_ID);
- }
-
private static void writePreviouslyUsedIds(@NonNull XmlSerializer parent,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) throws IOException {
final XmlSerializer serializer = parent.startTag(null, XML_TAG_PREVIOUSLY_USED_IDS);
@@ -509,4 +480,22 @@
throw new XmlPullParserException(
"Should be at the start of \"" + XML_TAG_ASSOCIATIONS + "\" tag");
}
+
+ private static @Nullable MacAddress stringToMacAddress(@Nullable String address) {
+ return address != null ? MacAddress.fromString(address) : null;
+ }
+
+ private static AssociationInfo createAssociationInfoNoThrow(int associationId,
+ @UserIdInt int userId, @NonNull String appPackage, @Nullable MacAddress macAddress,
+ @Nullable CharSequence displayName, @Nullable String profile, boolean selfManaged,
+ boolean notify, long timeApproved) {
+ AssociationInfo associationInfo = null;
+ try {
+ associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
+ displayName, profile, selfManaged, notify, timeApproved);
+ } catch (Exception e) {
+ if (DEBUG) Slog.w(LOG_TAG, "Could not create AssociationInfo", e);
+ }
+ return associationInfo;
+ }
}
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/RolesUtils.java
new file mode 100644
index 0000000..76340fc
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/RolesUtils.java
@@ -0,0 +1,96 @@
+/*
+ * 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.server.companion;
+
+import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP;
+
+import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
+import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.role.RoleManager;
+import android.companion.AssociationInfo;
+import android.content.Context;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.util.List;
+
+/** Utility methods for accessing {@link RoleManager} APIs. */
+final class RolesUtils {
+
+ static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
+ @NonNull String packageName, @NonNull String role) {
+ final RoleManager roleManager = context.getSystemService(RoleManager.class);
+ final List<String> roleHolders = roleManager.getRoleHoldersAsUser(
+ role, UserHandle.of(userId));
+ return roleHolders.contains(packageName);
+ }
+
+ static void addRoleHolderForAssociation(
+ @NonNull Context context, @NonNull AssociationInfo associationInfo) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
+ }
+
+ final String deviceProfile = associationInfo.getDeviceProfile();
+ if (deviceProfile == null) return;
+
+ final RoleManager roleManager = context.getSystemService(RoleManager.class);
+
+ final String packageName = associationInfo.getPackageName();
+ final int userId = associationInfo.getUserId();
+ final UserHandle userHandle = UserHandle.of(userId);
+
+ roleManager.addRoleHolderAsUser(deviceProfile, packageName,
+ MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
+ success -> {
+ if (!success) {
+ Slog.e(LOG_TAG, "Failed to add u" + userId + "\\" + packageName
+ + " to the list of " + deviceProfile + " holders.");
+ }
+ });
+ }
+
+ static void removeRoleHolderForAssociation(
+ @NonNull Context context, @NonNull AssociationInfo associationInfo) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo);
+ }
+
+ final String deviceProfile = associationInfo.getDeviceProfile();
+ if (deviceProfile == null) return;
+
+ final RoleManager roleManager = context.getSystemService(RoleManager.class);
+
+ final String packageName = associationInfo.getPackageName();
+ final int userId = associationInfo.getUserId();
+ final UserHandle userHandle = UserHandle.of(userId);
+
+ roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
+ MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
+ success -> {
+ if (!success) {
+ Slog.e(LOG_TAG, "Failed to remove u" + userId + "\\" + packageName
+ + " from the list of " + deviceProfile + " holders.");
+ }
+ });
+ }
+
+ private RolesUtils() {};
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9f22489..9351415 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -137,6 +137,7 @@
],
required: [
+ "default_television.xml",
"gps_debug.conf",
"protolog.conf.json.gz",
],
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 6ac015b..f3fad84 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.AppIdInt;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -108,7 +109,7 @@
// Please note the numbers should be continuous.
public static final int LAST_KNOWN_PACKAGE = PACKAGE_RECENTS;
- @IntDef(flag = true, prefix = "RESOLVE_", value = {
+ @LongDef(flag = true, prefix = "RESOLVE_", value = {
RESOLVE_NON_BROWSER_ONLY,
RESOLVE_NON_RESOLVER_ONLY
})
@@ -197,7 +198,7 @@
* @see PackageManager#getPackageInfo(String, int)
*/
public abstract PackageInfo getPackageInfo(String packageName,
- @PackageInfoFlags int flags, int filterCallingUid, int userId);
+ @PackageInfoFlags long flags, int filterCallingUid, int userId);
/**
* Retrieve CE data directory inode number of an application.
@@ -226,7 +227,7 @@
* deleted with {@code DELETE_KEEP_DATA} flag set).
*/
public abstract List<ApplicationInfo> getInstalledApplications(
- @ApplicationInfoFlags int flags, @UserIdInt int userId, int callingUid);
+ @ApplicationInfoFlags long flags, @UserIdInt int userId, int callingUid);
/**
* Retrieve launcher extras for a suspended package provided to the system in
@@ -323,7 +324,7 @@
* @see PackageManager#getPackageUidAsUser(String, int, int)
* @return The app's uid, or < 0 if the package was not found in that user
*/
- public abstract int getPackageUid(String packageName, @PackageInfoFlags int flags, int userId);
+ public abstract int getPackageUid(String packageName, @PackageInfoFlags long flags, int userId);
/**
* Retrieve all of the information we know about a particular package/application.
@@ -332,7 +333,7 @@
* @see PackageManager#getApplicationInfo(String, int)
*/
public abstract ApplicationInfo getApplicationInfo(String packageName,
- @ApplicationInfoFlags int flags, int filterCallingUid, int userId);
+ @ApplicationInfoFlags long flags, int filterCallingUid, int userId);
/**
* Retrieve all of the information we know about a particular activity class.
@@ -341,7 +342,7 @@
* @see PackageManager#getActivityInfo(ComponentName, int)
*/
public abstract ActivityInfo getActivityInfo(ComponentName component,
- @ComponentInfoFlags int flags, int filterCallingUid, int userId);
+ @ComponentInfoFlags long flags, int filterCallingUid, int userId);
/**
* Retrieve all activities that can be performed for the given intent.
@@ -352,7 +353,7 @@
* @see PackageManager#queryIntentActivities(Intent, int)
*/
public abstract List<ResolveInfo> queryIntentActivities(
- Intent intent, @Nullable String resolvedType, @ResolveInfoFlags int flags,
+ Intent intent, @Nullable String resolvedType, @ResolveInfoFlags long flags,
int filterCallingUid, int userId);
@@ -360,14 +361,14 @@
* Retrieve all receivers that can handle a broadcast of the given intent.
*/
public abstract List<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, int flags, int filterCallingUid, int userId);
+ String resolvedType, @ResolveInfoFlags long flags, int filterCallingUid, int userId);
/**
* Retrieve all services that can be performed for the given intent.
* @see PackageManager#queryIntentServices(Intent, int)
*/
public abstract List<ResolveInfo> queryIntentServices(
- Intent intent, int flags, int callingUid, int userId);
+ Intent intent, @ResolveInfoFlags long flags, int callingUid, int userId);
/**
* Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
@@ -591,20 +592,20 @@
* Resolves an activity intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, @PrivateResolveFlags int privateResolveFlags, int userId,
+ @ResolveInfoFlags long flags, @PrivateResolveFlags long privateResolveFlags, int userId,
boolean resolveForStart, int filterCallingUid);
/**
* Resolves a service intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
- int flags, int userId, int callingUid);
+ @ResolveInfoFlags long flags, int userId, int callingUid);
/**
* Resolves a content provider intent.
*/
- public abstract ProviderInfo resolveContentProvider(String name, int flags, int userId,
- int callingUid);
+ public abstract ProviderInfo resolveContentProvider(String name, @ComponentInfoFlags long flags,
+ int userId, int callingUid);
/**
* Track the creator of a new isolated uid.
@@ -875,8 +876,8 @@
throws IOException;
/** Returns {@code true} if the specified component is enabled and matches the given flags. */
- public abstract boolean isEnabledAndMatches(@NonNull ParsedMainComponent component, int flags,
- int userId);
+ public abstract boolean isEnabledAndMatches(@NonNull ParsedMainComponent component,
+ @ComponentInfoFlags long flags, int userId);
/** Returns {@code true} if the given user requires extra badging for icons. */
public abstract boolean userNeedsBadging(int userId);
@@ -1024,7 +1025,7 @@
* @param flags flags about the uninstall.
*/
public abstract void uninstallApex(String packageName, long versionCode, int userId,
- IntentSender intentSender, int flags);
+ IntentSender intentSender, @PackageManager.InstallFlags int installFlags);
/**
* Update fingerprint of build that updated the runtime permissions for a user.
@@ -1260,5 +1261,5 @@
/**
* Reconcile all app data for the given user.
*/
- public abstract void reconcileAppsData(int userId, int flags, boolean migrateAppsData);
+ public abstract void reconcileAppsData(int userId, int storageFlags, boolean migrateAppsData);
}
diff --git a/services/core/java/com/android/server/AppFuseMountException.java b/services/core/java/com/android/server/AppFuseMountException.java
new file mode 100644
index 0000000..9a9379e
--- /dev/null
+++ b/services/core/java/com/android/server/AppFuseMountException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.server;
+
+import android.os.Parcel;
+
+/**
+ * An exception that indicates there was an error with a
+ * app fuse mount operation.
+ */
+public class AppFuseMountException extends Exception {
+ public AppFuseMountException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ public AppFuseMountException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+
+ /**
+ * Rethrow as a {@link RuntimeException} subclass that is handled by
+ * {@link Parcel#writeException(Exception)}.
+ */
+ public IllegalArgumentException rethrowAsParcelableException() {
+ throw new IllegalStateException(getMessage(), this);
+ }
+}
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
index 197321f..263ff18 100644
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -35,6 +35,7 @@
* when Bluetooth is on and Bluetooth is in one of the following situations:
* 1. Bluetooth A2DP is connected.
* 2. Bluetooth Hearing Aid profile is connected.
+ * 3. Bluetooth LE Audio is connected
*/
class BluetoothAirplaneModeListener {
private static final String TAG = "BluetoothAirplaneModeListener";
@@ -132,7 +133,7 @@
return false;
}
if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn()
- || !mAirplaneHelper.isA2dpOrHearingAidConnected()) {
+ || !mAirplaneHelper.isMediaProfileConnected()) {
return false;
}
return true;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index f62935a..8860a81 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -35,6 +35,7 @@
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.IBluetooth;
@@ -456,12 +457,13 @@
}
}
} else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
- || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+ || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)
+ || BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_CONNECTED);
if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
&& state == BluetoothProfile.STATE_DISCONNECTED
- && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+ && !mBluetoothModeChangeHelper.isMediaProfileConnected()) {
Slog.i(TAG, "Device disconnected, reactivating pending flag changes");
onInitFlagsChanged();
}
@@ -2291,7 +2293,7 @@
Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED");
}
mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
- if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+ if (mBluetoothModeChangeHelper.isMediaProfileConnected()) {
Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+ DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+ " ms due to existing connections");
diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
index 3642e4d..e5854c9 100644
--- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java
+++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
@@ -20,6 +20,7 @@
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.Context;
@@ -37,6 +38,7 @@
public class BluetoothModeChangeHelper {
private volatile BluetoothA2dp mA2dp;
private volatile BluetoothHearingAid mHearingAid;
+ private volatile BluetoothLeAudio mLeAudio;
private final BluetoothAdapter mAdapter;
private final Context mContext;
@@ -47,6 +49,7 @@
mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
mAdapter.getProfileProxy(mContext, mProfileServiceListener,
BluetoothProfile.HEARING_AID);
+ mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.LE_AUDIO);
}
private final ServiceListener mProfileServiceListener = new ServiceListener() {
@@ -60,6 +63,9 @@
case BluetoothProfile.HEARING_AID:
mHearingAid = (BluetoothHearingAid) proxy;
break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudio = (BluetoothLeAudio) proxy;
+ break;
default:
break;
}
@@ -75,6 +81,9 @@
case BluetoothProfile.HEARING_AID:
mHearingAid = null;
break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudio = null;
+ break;
default:
break;
}
@@ -82,8 +91,8 @@
};
@VisibleForTesting
- public boolean isA2dpOrHearingAidConnected() {
- return isA2dpConnected() || isHearingAidConnected();
+ public boolean isMediaProfileConnected() {
+ return isA2dpConnected() || isHearingAidConnected() || isLeAudioConnected();
}
@VisibleForTesting
@@ -142,4 +151,12 @@
}
return hearingAid.getConnectedDevices().size() > 0;
}
+
+ private boolean isLeAudioConnected() {
+ final BluetoothLeAudio leAudio = mLeAudio;
+ if (leAudio == null) {
+ return false;
+ }
+ return leAudio.getConnectedDevices().size() > 0;
+ }
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index d04698c..25b36e8 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -19,9 +19,12 @@
import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
@@ -37,6 +40,8 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.provider.Settings;
import android.util.MutableBoolean;
import android.util.Slog;
@@ -87,6 +92,19 @@
*/
private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+ /** Action for starting emergency alerts on Wear OS. */
+ private static final String WEAR_LAUNCH_EMERGENCY_ACTION =
+ "com.android.systemui.action.LAUNCH_EMERGENCY";
+
+ /** Action for starting emergency alerts in retail mode on Wear OS. */
+ private static final String WEAR_LAUNCH_EMERGENCY_RETAIL_ACTION =
+ "com.android.systemui.action.LAUNCH_EMERGENCY_RETAIL";
+
+ /**
+ * Boolean extra for distinguishing intents coming from power button gesture.
+ */
+ private static final String EXTRA_LAUNCH_EMERGENCY_VIA_GESTURE = "launch_emergency_via_gesture";
+
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
private final CameraLiftTriggerEventListener mCameraLiftTriggerListener =
@@ -150,6 +168,9 @@
private int mPowerButtonSlowConsecutiveTaps;
private final UiEventLogger mUiEventLogger;
+ private boolean mHasFeatureWatch;
+ private long mVibrateMilliSecondsForPanicGesture;
+
@VisibleForTesting
public enum GestureLauncherEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "The user lifted the device just the right way to launch the camera.")
@@ -214,6 +235,16 @@
mUserId = ActivityManager.getCurrentUser();
mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
registerContentObservers();
+
+ mHasFeatureWatch =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ mVibrateMilliSecondsForPanicGesture =
+ resources.getInteger(
+ com.android
+ .internal
+ .R
+ .integer
+ .config_mashPressVibrateTimeOnPowerButton);
}
}
@@ -390,8 +421,7 @@
/**
* Whether to enable emergency gesture.
*/
- @VisibleForTesting
- static boolean isEmergencyGestureSettingEnabled(Context context, int userId) {
+ public static boolean isEmergencyGestureSettingEnabled(Context context, int userId) {
return isEmergencyGestureEnabled(context.getResources())
&& Settings.Secure.getIntForUser(context.getContentResolver(),
Settings.Secure.EMERGENCY_GESTURE_ENABLED, 1, userId) != 0;
@@ -475,7 +505,10 @@
if (mEmergencyGestureEnabled) {
// Commit to intercepting the powerkey event after the second "quick" tap to avoid
// lockscreen changes between launching camera and the emergency gesture flow.
- if (mPowerButtonConsecutiveTaps > 1) {
+ // Since watch doesn't have camera gesture, only intercept power key event after
+ // emergency gesture tap count.
+ if (mPowerButtonConsecutiveTaps
+ > (mHasFeatureWatch ? EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD : 1)) {
intercept = interactive;
}
if (mPowerButtonConsecutiveTaps == EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD) {
@@ -576,6 +609,12 @@
"userSetupComplete = %s, performing emergency gesture.",
userSetupComplete));
}
+
+ if (mHasFeatureWatch) {
+ onEmergencyGestureDetectedOnWatch();
+ return true;
+ }
+
StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
service.onEmergencyActionLaunchGestureDetected();
@@ -585,6 +624,37 @@
}
}
+ private void onEmergencyGestureDetectedOnWatch() {
+ Intent emergencyIntent =
+ new Intent(
+ isInRetailMode()
+ ? WEAR_LAUNCH_EMERGENCY_RETAIL_ACTION
+ : WEAR_LAUNCH_EMERGENCY_ACTION);
+ PackageManager pm = mContext.getPackageManager();
+ ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0);
+ if (resolveInfo == null) {
+ Slog.w(TAG, "Couldn't find an app to process the emergency intent "
+ + emergencyIntent.getAction());
+ return;
+ }
+
+ Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+ vibrator.vibrate(VibrationEffect.createOneShot(mVibrateMilliSecondsForPanicGesture,
+ VibrationEffect.DEFAULT_AMPLITUDE));
+
+ emergencyIntent.setComponent(
+ new ComponentName(
+ resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name));
+ emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ emergencyIntent.putExtra(EXTRA_LAUNCH_EMERGENCY_VIA_GESTURE, true);
+ mContext.startActivityAsUser(emergencyIntent, new UserHandle(mUserId));
+ }
+
+ private boolean isInRetailMode() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
+ }
+
private boolean isUserSetupComplete() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index c9608a5..3e02084 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -20,24 +20,27 @@
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
-import android.net.NetworkStack;
import android.net.Uri;
import android.net.nsd.INsdManager;
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.INsdServiceConnector;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Message;
-import android.os.Messenger;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Base64;
+import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -72,12 +75,11 @@
/**
* Clients receiving asynchronous messages
*/
- private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>();
+ private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
/* A map from unique id to client info */
private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
- private final AsyncChannel mReplyChannel = new AsyncChannel();
private final long mCleanupDelayMs;
private static final int INVALID_ID = 0;
@@ -149,65 +151,66 @@
class DefaultState extends State {
@Override
public boolean processMessage(Message msg) {
- ClientInfo cInfo = null;
+ final ClientInfo cInfo;
+ final int clientId = msg.arg2;
switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- AsyncChannel c = (AsyncChannel) msg.obj;
- if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
- c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
- cInfo = new ClientInfo(c, msg.replyTo);
- mClients.put(msg.replyTo, cInfo);
- } else {
- Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
+ case NsdManager.REGISTER_CLIENT:
+ final Pair<NsdServiceConnector, INsdManagerCallback> arg =
+ (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
+ final INsdManagerCallback cb = arg.second;
+ try {
+ cb.asBinder().linkToDeath(arg.first, 0);
+ cInfo = new ClientInfo(cb);
+ mClients.put(arg.first, cInfo);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Client " + clientId + " has already died");
}
break;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- switch (msg.arg1) {
- case AsyncChannel.STATUS_SEND_UNSUCCESSFUL:
- Slog.e(TAG, "Send failed, client connection lost");
- break;
- case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
- if (DBG) Slog.d(TAG, "Client disconnected");
- break;
- default:
- if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
- break;
- }
-
- cInfo = mClients.get(msg.replyTo);
+ case NsdManager.UNREGISTER_CLIENT:
+ final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
+ cInfo = mClients.remove(connector);
if (cInfo != null) {
cInfo.expungeAllRequests();
- mClients.remove(msg.replyTo);
if (cInfo.isLegacy()) {
mLegacyClientCount -= 1;
}
}
maybeScheduleStop();
break;
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
- AsyncChannel ac = new AsyncChannel();
- ac.connect(mContext, getHandler(), msg.replyTo);
- break;
case NsdManager.DISCOVER_SERVICES:
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onDiscoverServicesFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.STOP_DISCOVERY:
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onStopDiscoveryFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.REGISTER_SERVICE:
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.UNREGISTER_SERVICE:
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onUnregisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.RESOLVE_SERVICE:
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.DAEMON_CLEANUP:
mDaemon.maybeStop();
@@ -215,7 +218,7 @@
// This event should be only sent by the legacy (target SDK < S) clients.
// Mark the sending client as legacy.
case NsdManager.DAEMON_STARTUP:
- cInfo = mClients.get(msg.replyTo);
+ cInfo = getClientInfoForReply(msg);
if (cInfo != null) {
cancelStop();
cInfo.setLegacy();
@@ -230,6 +233,11 @@
}
return HANDLED;
}
+
+ private ClientInfo getClientInfoForReply(Message msg) {
+ final ListenerArgs args = (ListenerArgs) msg.obj;
+ return mClients.get(args.connector);
+ }
}
class DisabledState extends State {
@@ -289,122 +297,119 @@
@Override
public boolean processMessage(Message msg) {
- ClientInfo clientInfo;
- NsdServiceInfo servInfo;
- int id;
+ final ClientInfo clientInfo;
+ final int id;
+ final int clientId = msg.arg2;
+ final ListenerArgs args;
switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- return NOT_HANDLED;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- return NOT_HANDLED;
case NsdManager.DISABLE:
//TODO: cleanup clients
transitionTo(mDisabledState);
break;
case NsdManager.DISCOVER_SERVICES:
if (DBG) Slog.d(TAG, "Discover services");
- servInfo = (NsdServiceInfo) msg.obj;
- clientInfo = mClients.get(msg.replyTo);
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
if (requestLimitReached(clientInfo)) {
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_MAX_LIMIT);
+ clientInfo.onDiscoverServicesFailed(
+ clientId, NsdManager.FAILURE_MAX_LIMIT);
break;
}
maybeStartDaemon();
id = getUniqueId();
- if (discoverServices(id, servInfo.getServiceType())) {
+ if (discoverServices(id, args.serviceInfo.getServiceType())) {
if (DBG) {
Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
- servInfo.getServiceType());
+ args.serviceInfo.getServiceType());
}
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
+ storeRequestMap(clientId, id, clientInfo, msg.what);
+ clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
} else {
stopServiceDiscovery(id);
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
+ clientInfo.onDiscoverServicesFailed(clientId,
NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.STOP_DISCOVERY:
if (DBG) Slog.d(TAG, "Stop service discovery");
- clientInfo = mClients.get(msg.replyTo);
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
try {
- id = clientInfo.mClientIds.get(msg.arg2);
+ id = clientInfo.mClientIds.get(clientId);
} catch (NullPointerException e) {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onStopDiscoveryFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
}
- removeRequestMap(msg.arg2, id, clientInfo);
+ removeRequestMap(clientId, id, clientInfo);
if (stopServiceDiscovery(id)) {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
+ clientInfo.onStopDiscoverySucceeded(clientId);
} else {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onStopDiscoveryFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.REGISTER_SERVICE:
if (DBG) Slog.d(TAG, "Register service");
- clientInfo = mClients.get(msg.replyTo);
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
if (requestLimitReached(clientInfo)) {
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_MAX_LIMIT);
+ clientInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_MAX_LIMIT);
break;
}
maybeStartDaemon();
id = getUniqueId();
- if (registerService(id, (NsdServiceInfo) msg.obj)) {
- if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
+ if (registerService(id, args.serviceInfo)) {
+ if (DBG) Slog.d(TAG, "Register " + clientId + " " + id);
+ storeRequestMap(clientId, id, clientInfo, msg.what);
// Return success after mDns reports success
} else {
unregisterService(id);
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.UNREGISTER_SERVICE:
if (DBG) Slog.d(TAG, "unregister service");
- clientInfo = mClients.get(msg.replyTo);
- try {
- id = clientInfo.mClientIds.get(msg.arg2);
- } catch (NullPointerException e) {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
+ if (clientInfo == null) {
+ Slog.e(TAG, "Unknown connector in unregistration");
break;
}
- removeRequestMap(msg.arg2, id, clientInfo);
+ id = clientInfo.mClientIds.get(clientId);
+ removeRequestMap(clientId, id, clientInfo);
if (unregisterService(id)) {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
+ clientInfo.onUnregisterServiceSucceeded(clientId);
} else {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onUnregisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.RESOLVE_SERVICE:
if (DBG) Slog.d(TAG, "Resolve service");
- servInfo = (NsdServiceInfo) msg.obj;
- clientInfo = mClients.get(msg.replyTo);
-
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
if (clientInfo.mResolvedService != null) {
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_ALREADY_ACTIVE);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
break;
}
maybeStartDaemon();
id = getUniqueId();
- if (resolveService(id, servInfo)) {
+ if (resolveService(id, args.serviceInfo)) {
clientInfo.mResolvedService = new NsdServiceInfo();
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
+ storeRequestMap(clientId, id, clientInfo, msg.what);
} else {
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.NATIVE_DAEMON_EVENT:
@@ -449,30 +454,27 @@
case NativeResponseCode.SERVICE_FOUND:
/* NNN uniqueId serviceName regType domain */
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
- clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
- clientId, servInfo);
+ clientInfo.onServiceFound(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_LOST:
/* NNN uniqueId serviceName regType domain */
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
- clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
- clientId, servInfo);
+ clientInfo.onServiceLost(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
/* NNN uniqueId errorCode */
- clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onDiscoverServicesFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_REGISTERED:
/* NNN regId serviceName regType */
servInfo = new NsdServiceInfo(cooked[2], null);
- clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
- id, clientId, servInfo);
+ clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
/* NNN regId errorCode */
- clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_UPDATED:
/* NNN regId */
@@ -511,8 +513,8 @@
if (getAddrInfo(id2, cooked[3])) {
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
} else {
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
clientInfo.mResolvedService = null;
}
break;
@@ -521,26 +523,26 @@
stopResolveService(id);
removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null;
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null;
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
/* NNN resolveId hostname ttl addr */
try {
clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
- 0, clientId, clientInfo.mResolvedService);
+ clientInfo.onResolveServiceSucceeded(
+ clientId, clientInfo.mResolvedService);
} catch (java.net.UnknownHostException e) {
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
@@ -601,15 +603,71 @@
return service;
}
- public Messenger getMessenger() {
+ @Override
+ public INsdServiceConnector connect(INsdManagerCallback cb) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
- return new Messenger(mNsdStateMachine.getHandler());
+ final INsdServiceConnector connector = new NsdServiceConnector();
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
+ return connector;
}
- public void setEnabled(boolean isEnabled) {
- NetworkStack.checkNetworkStackPermission(mContext);
- mNsdSettings.putEnabledStatus(isEnabled);
- notifyEnabled(isEnabled);
+ private static class ListenerArgs {
+ public final NsdServiceConnector connector;
+ public final NsdServiceInfo serviceInfo;
+ ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
+ this.connector = connector;
+ this.serviceInfo = serviceInfo;
+ }
+ }
+
+ private class NsdServiceConnector extends INsdServiceConnector.Stub
+ implements IBinder.DeathRecipient {
+ @Override
+ public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.REGISTER_SERVICE, 0, listenerKey,
+ new ListenerArgs(this, serviceInfo)));
+ }
+
+ @Override
+ public void unregisterService(int listenerKey) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
+ new ListenerArgs(this, null)));
+ }
+
+ @Override
+ public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.DISCOVER_SERVICES, 0, listenerKey,
+ new ListenerArgs(this, serviceInfo)));
+ }
+
+ @Override
+ public void stopDiscovery(int listenerKey) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
+ }
+
+ @Override
+ public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.RESOLVE_SERVICE, 0, listenerKey,
+ new ListenerArgs(this, serviceInfo)));
+ }
+
+ @Override
+ public void startDaemon() {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
+ }
+
+ @Override
+ public void binderDied() {
+ mNsdStateMachine.sendMessage(
+ mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
+ }
}
private void notifyEnabled(boolean isEnabled) {
@@ -832,43 +890,11 @@
mNsdStateMachine.dump(fd, pw, args);
}
- /* arg2 on the source message has an id that needs to be retained in replies
- * see NsdManager for details */
- private Message obtainMessage(Message srcMsg) {
- Message msg = Message.obtain();
- msg.arg2 = srcMsg.arg2;
- return msg;
- }
-
- private void replyToMessage(Message msg, int what) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
- private void replyToMessage(Message msg, int what, int arg1) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- dstMsg.arg1 = arg1;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
- private void replyToMessage(Message msg, int what, Object obj) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- dstMsg.obj = obj;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
/* Information tracked per client */
private class ClientInfo {
private static final int MAX_LIMIT = 10;
- private final AsyncChannel mChannel;
- private final Messenger mMessenger;
+ private final INsdManagerCallback mCb;
/* Remembers a resolved service until getaddrinfo completes */
private NsdServiceInfo mResolvedService;
@@ -881,17 +907,14 @@
// The target SDK of this client < Build.VERSION_CODES.S
private boolean mIsLegacy = false;
- private ClientInfo(AsyncChannel c, Messenger m) {
- mChannel = c;
- mMessenger = m;
- if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
+ private ClientInfo(INsdManagerCallback cb) {
+ mCb = cb;
+ if (DBG) Slog.d(TAG, "New client");
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("mChannel ").append(mChannel).append("\n");
- sb.append("mMessenger ").append(mMessenger).append("\n");
sb.append("mResolvedService ").append(mResolvedService).append("\n");
sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
for(int i = 0; i< mClientIds.size(); i++) {
@@ -949,6 +972,102 @@
}
return mClientIds.keyAt(idx);
}
+
+ void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onDiscoverServicesStarted(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
+ }
+ }
+
+ void onDiscoverServicesFailed(int listenerKey, int error) {
+ try {
+ mCb.onDiscoverServicesFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
+ }
+ }
+
+ void onServiceFound(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onServiceFound(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onServiceFound(", e);
+ }
+ }
+
+ void onServiceLost(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onServiceLost(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onServiceLost(", e);
+ }
+ }
+
+ void onStopDiscoveryFailed(int listenerKey, int error) {
+ try {
+ mCb.onStopDiscoveryFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
+ }
+ }
+
+ void onStopDiscoverySucceeded(int listenerKey) {
+ try {
+ mCb.onStopDiscoverySucceeded(listenerKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
+ }
+ }
+
+ void onRegisterServiceFailed(int listenerKey, int error) {
+ try {
+ mCb.onRegisterServiceFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onRegisterServiceFailed", e);
+ }
+ }
+
+ void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onRegisterServiceSucceeded(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
+ }
+ }
+
+ void onUnregisterServiceFailed(int listenerKey, int error) {
+ try {
+ mCb.onUnregisterServiceFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
+ }
+ }
+
+ void onUnregisterServiceSucceeded(int listenerKey) {
+ try {
+ mCb.onUnregisterServiceSucceeded(listenerKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
+ }
+ }
+
+ void onResolveServiceFailed(int listenerKey, int error) {
+ try {
+ mCb.onResolveServiceFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onResolveServiceFailed", e);
+ }
+ }
+
+ void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onResolveServiceSucceeded(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 6f009b6..1929df8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1223,7 +1223,7 @@
}
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
- if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {
+ if (vol.isVisibleForUser(userId) && vol.isMountedReadable()) {
final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
@@ -1570,7 +1570,7 @@
|| Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
}
@@ -1580,13 +1580,13 @@
&& vol.disk.isDefaultPrimary()) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
}
// Adoptable public disks are visible to apps, since they meet
// public API requirement of being in a stable location.
if (vol.disk.isAdoptable()) {
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
}
vol.mountUserId = mCurrentUserId;
@@ -1597,7 +1597,7 @@
} else if (vol.type == VolumeInfo.TYPE_STUB) {
if (vol.disk.isStubVisible()) {
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
}
vol.mountUserId = mCurrentUserId;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
@@ -1744,7 +1744,7 @@
// started after this point will trigger additional
// user-specific broadcasts.
for (int userId : mSystemUnlockedUsers) {
- if (vol.isVisibleForRead(userId)) {
+ if (vol.isVisibleForUser(userId)) {
final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
@@ -3565,24 +3565,24 @@
}
@Override
- public ParcelFileDescriptor open() throws NativeDaemonConnectorException {
+ public ParcelFileDescriptor open() throws AppFuseMountException {
try {
final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
mMounted = true;
return new ParcelFileDescriptor(fd);
} catch (Exception e) {
- throw new NativeDaemonConnectorException("Failed to mount", e);
+ throw new AppFuseMountException("Failed to mount", e);
}
}
@Override
public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
- throws NativeDaemonConnectorException {
+ throws AppFuseMountException {
try {
return new ParcelFileDescriptor(
mVold.openAppFuseFile(uid, mountId, fileId, flags));
} catch (Exception e) {
- throw new NativeDaemonConnectorException("Failed to open", e);
+ throw new AppFuseMountException("Failed to open", e);
}
}
@@ -3622,7 +3622,7 @@
// It seems the thread of mAppFuseBridge has already been terminated.
mAppFuseBridge = null;
}
- } catch (NativeDaemonConnectorException e) {
+ } catch (AppFuseMountException e) {
throw e.rethrowAsParcelableException();
}
}
@@ -3783,7 +3783,7 @@
if (forWrite) {
match = vol.isVisibleForWrite(userId);
} else {
- match = vol.isVisibleForRead(userId)
+ match = vol.isVisibleForUser(userId)
|| (includeInvisible && vol.getPath() != null);
}
if (!match) continue;
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index b068f86..0c990ec 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -141,7 +141,7 @@
* | or its properties
* v |
* +-----------------------------------------------------------------------+
- * | UnderlyingNetworkTracker |
+ * | UnderlyingNetworkController |
* | |
* | Manages lifecycle of underlying physical networks, filing requests to |
* | bring them up, and releasing them as they become no longer necessary |
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index b019789..ac20a08 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -685,6 +685,7 @@
final UUID errorId = mTraceErrorLogger.generateErrorId();
if (mTraceErrorLogger.isAddErrorIdEnabled()) {
mTraceErrorLogger.addErrorIdToTrace("system_server", errorId);
+ mTraceErrorLogger.addSubjectToTrace(subject, errorId);
}
// Log the atom as early as possible since it is used as a mechanism to trigger
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6decdb9..7993936 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -158,6 +158,7 @@
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.SomeArgs;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
@@ -4524,9 +4525,12 @@
if (r.app != null) {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
- msg.obj = r.app;
- msg.getData().putCharSequence(
- ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = r.app;
+ args.arg2 = r.toString();
+ args.arg3 = r.getComponentName();
+
+ msg.obj = args;
mAm.mHandler.sendMessage(msg);
}
}
@@ -5691,11 +5695,14 @@
}
}
- void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
- mAm.crashApplicationWithType(app.uid, app.getPid(), app.info.packageName, app.userId,
+ void serviceForegroundCrash(ProcessRecord app, String serviceRecord,
+ ComponentName service) {
+ mAm.crashApplicationWithTypeWithExtras(
+ app.uid, app.getPid(), app.info.packageName, app.userId,
"Context.startForegroundService() did not then call Service.startForeground(): "
+ serviceRecord, false /*force*/,
- ForegroundServiceDidNotStartInTimeException.TYPE_ID);
+ ForegroundServiceDidNotStartInTimeException.TYPE_ID,
+ ForegroundServiceDidNotStartInTimeException.createExtrasForService(service));
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1fe4d14..a33aa60 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -345,6 +345,7 @@
import com.android.internal.os.ByteTransferPipe;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.SomeArgs;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
import com.android.internal.policy.AttributeCache;
@@ -1511,8 +1512,6 @@
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
- static final String SERVICE_RECORD_KEY = "servicerecord";
-
/**
* Flag whether the current user is a "monkey", i.e. whether
* the UI is driven by a UI automation tool.
@@ -1691,8 +1690,12 @@
mServices.serviceForegroundTimeout((ServiceRecord) msg.obj);
} break;
case SERVICE_FOREGROUND_CRASH_MSG: {
- mServices.serviceForegroundCrash((ProcessRecord) msg.obj,
- msg.getData().getCharSequence(SERVICE_RECORD_KEY));
+ SomeArgs args = (SomeArgs) msg.obj;
+ mServices.serviceForegroundCrash(
+ (ProcessRecord) args.arg1,
+ (String) args.arg2,
+ (ComponentName) args.arg3);
+ args.recycle();
} break;
case UPDATE_TIME_ZONE: {
synchronized (mProcLock) {
@@ -3048,6 +3051,14 @@
@Override
public void crashApplicationWithType(int uid, int initialPid, String packageName, int userId,
String message, boolean force, int exceptionTypeId) {
+ crashApplicationWithTypeWithExtras(uid, initialPid, packageName, userId, message,
+ force, exceptionTypeId, null);
+ }
+
+ @Override
+ public void crashApplicationWithTypeWithExtras(int uid, int initialPid, String packageName,
+ int userId, String message, boolean force, int exceptionTypeId,
+ @Nullable Bundle extras) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: crashApplication() from pid="
@@ -3060,7 +3071,7 @@
synchronized(this) {
mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId,
- message, force, exceptionTypeId);
+ message, force, exceptionTypeId, extras);
}
}
@@ -4288,7 +4299,7 @@
didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId,
ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
- evenPersistent, true /* setRemoved */,
+ evenPersistent, true /* setRemoved */, uninstalling,
packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
: ApplicationExitInfo.REASON_USER_REQUESTED,
ApplicationExitInfo.SUBREASON_UNKNOWN,
@@ -7371,6 +7382,7 @@
ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
true /* callerWillRestart */, true /* doit */,
true /* evenPersistent */, false /* setRemoved */,
+ false /* uninstalling */,
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_KILL_UID,
reason != null ? reason : "kill uid");
@@ -7392,6 +7404,7 @@
ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
true /* callerWillRestart */, true /* doit */,
true /* evenPersistent */, false /* setRemoved */,
+ false /* uninstalling */,
ApplicationExitInfo.REASON_PERMISSION_CHANGE,
ApplicationExitInfo.SUBREASON_UNKNOWN,
reason != null ? reason : "kill uid");
@@ -12690,30 +12703,38 @@
"Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
+ "flag");
}
- if (CompatChanges.isChangeEnabled(DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED,
- callingUid)
- && !explicitExportStateDefined) {
- if (ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT) {
- throw new SecurityException(
- callerPackage + ": Targeting T+ (version "
- + Build.VERSION_CODES.TIRAMISU
- + " and above) requires that one of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED be specified when registering a "
- + "receiver");
- } else {
- Slog.wtf(TAG,
- callerPackage + ": Targeting T+ (version "
- + Build.VERSION_CODES.TIRAMISU
- + " and above) requires that one of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED be specified when registering a "
- + "receiver");
- // Assume default behavior-- flag check is not enforced
+
+ // Don't enforce the flag check if we're EITHER registering for only protected
+ // broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
+ // not be used generally, so we will be marking them as exported by default
+ final boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
+ DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
+ if (!onlyProtectedBroadcasts) {
+ if (receiver == null && !explicitExportStateDefined) {
+ // sticky broadcast, no flag specified (flag isn't required)
+ flags |= Context.RECEIVER_EXPORTED;
+ } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {
+ if (ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT) {
+ throw new SecurityException(
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ } else {
+ Slog.wtf(TAG,
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ // Assume default behavior-- flag check is not enforced
+ flags |= Context.RECEIVER_EXPORTED;
+ }
+ } else if (!requireExplicitFlagForDynamicReceivers) {
+ // Change is not enabled, thus not targeting T+. Assume exported.
flags |= Context.RECEIVER_EXPORTED;
}
- } else if (!CompatChanges.isChangeEnabled(DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED,
- callingUid)) {
- // Change is not enabled, thus not targeting T+. Assume exported.
- flags |= Context.RECEIVER_EXPORTED;
}
}
@@ -12731,7 +12752,7 @@
(intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
continue;
}
- // If intent has scheme "content", it will need to acccess
+ // If intent has scheme "content", it will need to access
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
// cannot lock ActivityManagerService here.
@@ -15409,6 +15430,16 @@
}
@Override
+ public String getSwitchingFromUserMessage() {
+ return mUserController.getSwitchingFromSystemUserMessage();
+ }
+
+ @Override
+ public String getSwitchingToUserMessage() {
+ return mUserController.getSwitchingToSystemUserMessage();
+ }
+
+ @Override
public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
mUserController.setStopUserOnSwitch(value);
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 0dfdfe9..6c1a00d 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -27,6 +27,7 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AnrController;
@@ -40,6 +41,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
@@ -489,7 +491,7 @@
* @param message
*/
void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId,
- String message, boolean force, int exceptionTypeId) {
+ String message, boolean force, int exceptionTypeId, @Nullable Bundle extras) {
ProcessRecord proc = null;
// Figure out which process to kill. We don't trust that initialPid
@@ -521,7 +523,7 @@
return;
}
- proc.scheduleCrashLocked(message, exceptionTypeId);
+ proc.scheduleCrashLocked(message, exceptionTypeId, extras);
if (force) {
// If the app is responsive, the scheduled crash will happen as expected
// and then the delayed summary kill will be a no-op.
diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
index 7b5f2cd..259dd8ec 100644
--- a/services/core/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/core/java/com/android/server/am/BaseErrorDialog.java
@@ -53,7 +53,7 @@
mHandler.sendEmptyMessage(DISABLE_BUTTONS);
mHandler.sendMessageDelayed(mHandler.obtainMessage(ENABLE_BUTTONS), 1000);
getContext().registerReceiver(mReceiver,
- new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), Context.RECEIVER_EXPORTED);
}
@Override
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 17daa75..91d6488 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -615,7 +615,7 @@
Slog.w(TAG, "Can't deliver broadcast to " + app.processName
+ " (pid " + app.getPid() + "). Crashing it.");
app.scheduleCrashLocked("can't deliver broadcast",
- CannotDeliverBroadcastException.TYPE_ID);
+ CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
}
throw ex;
}
@@ -789,7 +789,7 @@
// Ensure that broadcasts are only sent to other apps if they are explicitly marked as
// exported, or are System level broadcasts
- if (!skip && !filter.exported && Process.SYSTEM_UID != r.callingUid
+ if (!skip && !filter.exported && !Process.isCoreUid(r.callingUid)
&& filter.receiverList.uid != r.callingUid) {
Slog.w(TAG, "Exported Denial: sending "
@@ -800,7 +800,7 @@
+ " due to receiver " + filter.receiverList.app
+ " (uid " + filter.receiverList.uid + ")"
+ " not specifying RECEIVER_EXPORTED");
- skip = true;
+ // skip = true;
}
if (skip) {
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 4220506..18ad1f5 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -286,6 +286,7 @@
&& mService.mTraceErrorLogger.isAddErrorIdEnabled()) {
errorId = mService.mTraceErrorLogger.generateErrorId();
mService.mTraceErrorLogger.addErrorIdToTrace(mApp.processName, errorId);
+ mService.mTraceErrorLogger.addSubjectToTrace(annotation, errorId);
} else {
errorId = null;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bbd41f7..80a8d63 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -135,7 +135,6 @@
import com.android.server.am.ActivityManagerService.ProcessChangeItem;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.DexManager;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowManagerService;
@@ -2794,8 +2793,8 @@
int reasonCode, int subReason, String reason) {
return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
- false /* evenPersistent */, false /* setRemoved */, reasonCode,
- subReason, reason);
+ false /* evenPersistent */, false /* setRemoved */, false /* uninstalling */,
+ reasonCode, subReason, reason);
}
@GuardedBy("mService")
@@ -2828,9 +2827,10 @@
@GuardedBy({"mService", "mProcLock"})
boolean killPackageProcessesLSP(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
- boolean doit, boolean evenPersistent, boolean setRemoved, int reasonCode,
- int subReason, String reason) {
- ArrayList<ProcessRecord> procs = new ArrayList<>();
+ boolean doit, boolean evenPersistent, boolean setRemoved, boolean uninstalling,
+ int reasonCode, int subReason, String reason) {
+ final PackageManagerInternal pm = mService.getPackageManagerInternal();
+ final ArrayList<Pair<ProcessRecord, Boolean>> procs = new ArrayList<>();
// Remove all processes this package may have touched: all with the
// same UID (except for the system or root user), and all whose name
@@ -2847,7 +2847,18 @@
}
if (app.isRemoved()) {
if (doit) {
- procs.add(app);
+ boolean shouldAllowRestart = false;
+ if (!uninstalling && packageName != null) {
+ // This package has a dependency on the given package being stopped,
+ // while it's not being frozen nor uninstalled, allow to restart it.
+ shouldAllowRestart = !app.getPkgList().containsKey(packageName)
+ && app.getPkgDeps() != null
+ && app.getPkgDeps().contains(packageName)
+ && app.info != null
+ && !pm.isPackageFrozen(app.info.packageName, app.uid,
+ app.userId);
+ }
+ procs.add(new Pair<>(app, shouldAllowRestart));
}
continue;
}
@@ -2862,6 +2873,8 @@
continue;
}
+ boolean shouldAllowRestart = false;
+
// If no package is specified, we call all processes under the
// give user id.
if (packageName == null) {
@@ -2883,9 +2896,16 @@
if (userId != UserHandle.USER_ALL && app.userId != userId) {
continue;
}
- if (!app.getPkgList().containsKey(packageName) && !isDep) {
+ final boolean isInPkgList = app.getPkgList().containsKey(packageName);
+ if (!isInPkgList && !isDep) {
continue;
}
+ if (!isInPkgList && isDep && !uninstalling && app.info != null
+ && !pm.isPackageFrozen(app.info.packageName, app.uid, app.userId)) {
+ // This package has a dependency on the given package being stopped,
+ // while it's not being frozen nor uninstalled, allow to restart it.
+ shouldAllowRestart = true;
+ }
}
// Process has passed all conditions, kill it!
@@ -2895,13 +2915,14 @@
if (setRemoved) {
app.setRemoved(true);
}
- procs.add(app);
+ procs.add(new Pair<>(app, shouldAllowRestart));
}
}
int N = procs.size();
for (int i=0; i<N; i++) {
- removeProcessLocked(procs.get(i), callerWillRestart, allowRestart,
+ final Pair<ProcessRecord, Boolean> proc = procs.get(i);
+ removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
reasonCode, subReason, reason);
}
killAppZygotesLocked(packageName, appId, userId, false /* force */);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index eba02f10..c830554 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -31,6 +31,7 @@
import android.content.pm.VersionedPackage;
import android.content.res.CompatibilityInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -985,7 +986,7 @@
* of its subclasses.
*/
@GuardedBy("mService")
- void scheduleCrashLocked(String message, int exceptionTypeId) {
+ void scheduleCrashLocked(String message, int exceptionTypeId, @Nullable Bundle extras) {
// Checking killedbyAm should keep it from showing the crash dialog if the process
// was already dead for a good / normal reason.
if (!mKilledByAm) {
@@ -996,7 +997,7 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- mThread.scheduleCrash(message, exceptionTypeId);
+ mThread.scheduleCrash(message, exceptionTypeId, extras);
} catch (RemoteException e) {
// If it's already dead our work is done. If it's wedged just kill it.
// We won't get the crash dialog or the error reporting.
diff --git a/services/core/java/com/android/server/am/TraceErrorLogger.java b/services/core/java/com/android/server/am/TraceErrorLogger.java
index c658100..29a9b5c 100644
--- a/services/core/java/com/android/server/am/TraceErrorLogger.java
+++ b/services/core/java/com/android/server/am/TraceErrorLogger.java
@@ -54,4 +54,17 @@
COUNTER_PREFIX + processName + "#" + errorId.toString(),
PLACEHOLDER_VALUE);
}
+
+ /**
+ * Pushes a counter containing an ANR/Watchdog subject and a unique id so that the subject
+ * can be uniquely identified.
+ *
+ * @param subject The subject to include in the trace.
+ * @param errorId The unique id with which to tag the trace.
+ */
+ public void addSubjectToTrace(String subject, UUID errorId) {
+ Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ String.format("Subject(for ErrorId %s):%s", errorId.toString(), subject),
+ PLACEHOLDER_VALUE);
+ }
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d52f52e..e79cba1 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -67,10 +67,10 @@
import android.content.PermissionChecker;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackagePartitions;
import android.content.pm.UserInfo;
import android.os.BatteryStats;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -719,7 +719,7 @@
// purposefully block sending BOOT_COMPLETED until after all
// PRE_BOOT receivers are finished to avoid ANR'ing apps
final UserInfo info = getUserInfo(userId);
- if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)
+ if (!Objects.equals(info.lastLoggedInFingerprint, PackagePartitions.FINGERPRINT)
|| SystemProperties.getBoolean("persist.pm.mock-upgrade", false)) {
// Suppress double notifications for managed profiles that
// were unlocked automatically as part of their parent user
@@ -1809,7 +1809,8 @@
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
- getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage());
+ getSwitchingFromSystemUserMessageUnchecked(),
+ getSwitchingToSystemUserMessageUnchecked());
}
private void dispatchForegroundProfileChanged(@UserIdInt int userId) {
@@ -2622,18 +2623,40 @@
}
}
- private String getSwitchingFromSystemUserMessage() {
+ // Called by AMS, must check permission
+ String getSwitchingFromSystemUserMessage() {
+ checkHasManageUsersPermission("getSwitchingFromSystemUserMessage()");
+
+ return getSwitchingFromSystemUserMessageUnchecked();
+ }
+
+ // Called by AMS, must check permission
+ String getSwitchingToSystemUserMessage() {
+ checkHasManageUsersPermission("getSwitchingToSystemUserMessage()");
+
+ return getSwitchingToSystemUserMessageUnchecked();
+ }
+
+ private String getSwitchingFromSystemUserMessageUnchecked() {
synchronized (mLock) {
return mSwitchingFromSystemUserMessage;
}
}
- private String getSwitchingToSystemUserMessage() {
+ private String getSwitchingToSystemUserMessageUnchecked() {
synchronized (mLock) {
return mSwitchingToSystemUserMessage;
}
}
+ private void checkHasManageUsersPermission(String operation) {
+ if (mInjector.checkCallingPermission(
+ android.Manifest.permission.MANAGE_USERS) == PackageManager.PERMISSION_DENIED) {
+ throw new SecurityException(
+ "You need MANAGE_USERS permission to call " + operation);
+ }
+ }
+
void dumpDebug(ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
long token = proto.start(fieldId);
@@ -2706,6 +2729,12 @@
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
pw.println(" mInitialized:" + mInitialized);
+ if (mSwitchingFromSystemUserMessage != null) {
+ pw.println(" mSwitchingFromSystemUserMessage: " + mSwitchingFromSystemUserMessage);
+ }
+ if (mSwitchingToSystemUserMessage != null) {
+ pw.println(" mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 61b8ded..7341e74 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -251,7 +251,7 @@
"Successful background authentication!");
}
- mAlreadyDone = true;
+ markAlreadyDone();
if (mTaskStackListener != null) {
mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
@@ -327,7 +327,7 @@
final @LockoutTracker.LockoutMode int lockoutMode =
handleFailedAttempt(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
- mAlreadyDone = true;
+ markAlreadyDone();
}
final CoexCoordinator coordinator = CoexCoordinator.getInstance();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 9764a16..b73e911 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -114,7 +114,7 @@
// Currently only used for authentication client. The cookie generated by BiometricService
// is never 0.
private final int mCookie;
- boolean mAlreadyDone;
+ private boolean mAlreadyDone = false;
// Use an empty callback by default since delayed operations can receive events
// before they are started and cause NPE in subclasses that access this field directly.
@@ -202,11 +202,9 @@
return callback;
}
- public boolean isAlreadyDone() {
- return mAlreadyDone;
- }
-
- public void destroy() {
+ /** Signals this operation has completed its lifecycle and should no longer be used. */
+ void destroy() {
+ mAlreadyDone = true;
if (mToken != null) {
try {
mToken.unlinkToDeath(this, 0);
@@ -218,6 +216,20 @@
}
}
+ /**
+ * Call while the operation is still active, but nearly done, to prevent any action
+ * upon client death (only needed for authentication clients).
+ */
+ void markAlreadyDone() {
+ Slog.d(TAG, "marking operation as done: " + this);
+ mAlreadyDone = true;
+ }
+
+ /** If this operation has been marked as completely done (or cancelled). */
+ public boolean isAlreadyDone() {
+ return mAlreadyDone;
+ }
+
@Override
public void binderDied() {
binderDiedInternal(true /* clearListener */);
@@ -225,10 +237,9 @@
// TODO(b/157790417): Move this to the scheduler
void binderDiedInternal(boolean clearListener) {
- Slog.e(TAG, "Binder died, owner: " + getOwnerString()
- + ", operation: " + this.getClass().getName());
+ Slog.e(TAG, "Binder died, operation: " + this);
- if (isAlreadyDone()) {
+ if (mAlreadyDone) {
Slog.w(TAG, "Binder died but client is finished, ignoring");
return;
}
@@ -299,7 +310,7 @@
@Override
public String toString() {
return "{[" + mSequentialId + "] "
- + this.getClass().getSimpleName()
+ + this.getClass().getName()
+ ", proto=" + getProtoEnum()
+ ", owner=" + getOwnerString()
+ ", cookie=" + getCookie()
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 361ec40..a358bc2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -605,6 +605,9 @@
if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
// We can set it to null immediately, since the HAL was never notified to start.
+ if (mCurrentOperation != null) {
+ mCurrentOperation.mClientMonitor.destroy();
+ }
mCurrentOperation = null;
startNextOperationIfIdle();
return;
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 74a21a7..beb4d5b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -60,17 +60,62 @@
private static final Plog PLOG = Plog.createSystemPlog(TAG);
+ /**
+ * Creates a BrightnessMappingStrategy for active (normal) mode.
+ * @param resources
+ * @param displayDeviceConfig
+ * @return the BrightnessMappingStrategy
+ */
@Nullable
public static BrightnessMappingStrategy create(Resources resources,
DisplayDeviceConfig displayDeviceConfig) {
+ return create(resources, displayDeviceConfig, /* isForIdleMode= */ false);
+ }
- // Display independent values
- float[] luxLevels = getLuxLevels(resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLevels));
+ /**
+ * Creates a BrightnessMappingStrategy for idle screen brightness mode.
+ * @param resources
+ * @param displayDeviceConfig
+ * @return the BrightnessMappingStrategy
+ */
+ @Nullable
+ public static BrightnessMappingStrategy createForIdleMode(Resources resources,
+ DisplayDeviceConfig displayDeviceConfig) {
+ return create(resources, displayDeviceConfig, /* isForIdleMode= */ true);
+ }
+
+ /**
+ * Creates a BrightnessMapping strategy for either active or idle screen brightness mode.
+ * We do not create a simple mapping strategy for idle mode.
+ *
+ * @param resources
+ * @param displayDeviceConfig
+ * @param isForIdleMode determines whether the configurations loaded are for idle screen
+ * brightness mode or active screen brightness mode.
+ * @return the BrightnessMappingStrategy
+ */
+ @Nullable
+ private static BrightnessMappingStrategy create(Resources resources,
+ DisplayDeviceConfig displayDeviceConfig, boolean isForIdleMode) {
+
+ // Display independent, mode dependent values
+ float[] brightnessLevelsNits;
+ float[] luxLevels;
+ if (isForIdleMode) {
+ brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle));
+ luxLevels = getLuxLevels(resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevelsIdle));
+ } else {
+ brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
+ luxLevels = getLuxLevels(resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevels));
+ }
+
+ // Display independent, mode independent values
int[] brightnessLevelsBacklight = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
- float[] brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
1, 1);
@@ -91,7 +136,7 @@
builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
autoBrightnessAdjustmentMaxGamma);
- } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
+ } else if (isValidMapping(luxLevels, brightnessLevelsBacklight) && !isForIdleMode) {
return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout);
} else {
diff --git a/services/core/java/com/android/server/display/DensityMap.java b/services/core/java/com/android/server/display/DensityMap.java
new file mode 100644
index 0000000..4aafd14
--- /dev/null
+++ b/services/core/java/com/android/server/display/DensityMap.java
@@ -0,0 +1,137 @@
+/*
+ * 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.server.display;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Class which can compute the logical density for a display resolution. It holds a collection
+ * of pre-configured densities, which are used for look-up and interpolation.
+ */
+public class DensityMap {
+
+ // Instead of resolutions we store the squared diagonal size. Diagonals make the map
+ // keys invariant to rotations and are useful for interpolation because they're scalars.
+ // Squared diagonals have the same properties as diagonals (the square function is monotonic)
+ // but also allow us to use integer types and avoid floating point arithmetics.
+ private final Entry[] mSortedDensityMapEntries;
+
+ /**
+ * Creates a density map. The newly created object takes ownership of the passed array.
+ */
+ static DensityMap createByOwning(Entry[] densityMapEntries) {
+ return new DensityMap(densityMapEntries);
+ }
+
+ private DensityMap(Entry[] densityMapEntries) {
+ Arrays.sort(densityMapEntries, Comparator.comparingInt(entry -> entry.squaredDiagonal));
+ mSortedDensityMapEntries = densityMapEntries;
+ verifyDensityMap(mSortedDensityMapEntries);
+ }
+
+ /**
+ * Returns the logical density for the given resolution.
+ *
+ * If the resolution matches one of the entries in the map, the corresponding density is
+ * returned. Otherwise the return value is interpolated using the closest entries in the map.
+ */
+ public int getDensityForResolution(int width, int height) {
+ int squaredDiagonal = width * width + height * height;
+
+ // Search for two pre-configured entries "left" and "right" with the following criteria
+ // * left <= squaredDiagonal
+ // * squaredDiagonal - left is minimal
+ // * right > squaredDiagonal
+ // * right - squaredDiagonal is minimal
+ Entry left = Entry.ZEROES;
+ Entry right = null;
+
+ for (Entry entry : mSortedDensityMapEntries) {
+ if (entry.squaredDiagonal <= squaredDiagonal) {
+ left = entry;
+ } else {
+ right = entry;
+ break;
+ }
+ }
+
+ // Check if we found an exact match.
+ if (left.squaredDiagonal == squaredDiagonal) {
+ return left.density;
+ }
+
+ // If no configured resolution is higher than the specified resolution, interpolate
+ // between (0,0) and (maxConfiguredDiagonal, maxConfiguredDensity).
+ if (right == null) {
+ right = left; // largest entry in the sorted array
+ left = Entry.ZEROES;
+ }
+
+ double leftDiagonal = Math.sqrt(left.squaredDiagonal);
+ double rightDiagonal = Math.sqrt(right.squaredDiagonal);
+ double diagonal = Math.sqrt(squaredDiagonal);
+
+ return (int) Math.round((diagonal - leftDiagonal) * (right.density - left.density)
+ / (rightDiagonal - leftDiagonal) + left.density);
+ }
+
+ private static void verifyDensityMap(Entry[] sortedEntries) {
+ for (int i = 1; i < sortedEntries.length; i++) {
+ Entry prev = sortedEntries[i - 1];
+ Entry curr = sortedEntries[i];
+
+ if (prev.squaredDiagonal == curr.squaredDiagonal) {
+ // This will most often happen because there are two entries with the same
+ // resolution (AxB and AxB) or rotated resolution (AxB and BxA), but it can also
+ // happen in the very rare cases when two different resolutions happen to have
+ // the same diagonal (e.g. 100x700 and 500x500).
+ throw new IllegalStateException("Found two entries in the density map with"
+ + " the same diagonal: " + prev + ", " + curr);
+ } else if (prev.density > curr.density) {
+ throw new IllegalStateException("Found two entries in the density map with"
+ + " increasing diagonal but decreasing density: " + prev + ", " + curr);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DensityMap{"
+ + "mDensityMapEntries=" + Arrays.toString(mSortedDensityMapEntries)
+ + '}';
+ }
+
+ static class Entry {
+ public static final Entry ZEROES = new Entry(0, 0, 0);
+
+ public final int squaredDiagonal;
+ public final int density;
+
+ Entry(int width, int height, int density) {
+ this.squaredDiagonal = width * width + height * height;
+ this.density = density;
+ }
+
+ @Override
+ public String toString() {
+ return "DensityMapEntry{"
+ + "squaredDiagonal=" + squaredDiagonal
+ + ", density=" + density + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 2ae5cbb..a9e1647 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
@@ -31,6 +32,7 @@
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.config.Density;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
@@ -52,6 +54,7 @@
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -70,6 +73,8 @@
private static final String ETC_DIR = "etc";
private static final String DISPLAY_CONFIG_DIR = "displayconfig";
private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
+ private static final String DEFAULT_CONFIG_FILE = "default.xml";
+ private static final String DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT = "default_%s.xml";
private static final String PORT_SUFFIX_FORMAT = "port_%d";
private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
private static final String NO_SUFFIX_FORMAT = "%d";
@@ -121,6 +126,7 @@
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
+ private DensityMap mDensityMap;
private String mLoadedFrom = null;
private DisplayDeviceConfig(Context context) {
@@ -141,6 +147,33 @@
*/
public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
boolean isDefaultDisplay) {
+ final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
+ isDefaultDisplay);
+
+ config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
+ return config;
+ }
+
+ /**
+ * Creates an instance using global values since no display device config xml exists.
+ * Uses values from config or PowerManager.
+ *
+ * @param context
+ * @param useConfigXml
+ * @return A configuration instance.
+ */
+ public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
+ final DisplayDeviceConfig config;
+ if (useConfigXml) {
+ config = getConfigFromGlobalXml(context);
+ } else {
+ config = getConfigFromPmValues(context);
+ }
+ return config;
+ }
+
+ private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
+ long physicalDisplayId, boolean isDefaultDisplay) {
DisplayDeviceConfig config;
config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
@@ -161,22 +194,53 @@
return create(context, isDefaultDisplay);
}
- /**
- * Creates an instance using global values since no display device config xml exists.
- * Uses values from config or PowerManager.
- *
- * @param context
- * @param useConfigXml
- * @return A configuration instance.
- */
- public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
- DisplayDeviceConfig config;
- if (useConfigXml) {
- config = getConfigFromGlobalXml(context);
- } else {
- config = getConfigFromPmValues(context);
+ private static DisplayConfiguration loadDefaultConfigurationXml(Context context) {
+ List<File> defaultXmlLocations = new ArrayList<>();
+ defaultXmlLocations.add(Environment.buildPath(Environment.getProductDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+ defaultXmlLocations.add(Environment.buildPath(Environment.getVendorDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+
+ // Read config_defaultUiModeType directly because UiModeManager hasn't started yet.
+ final int uiModeType = context.getResources()
+ .getInteger(com.android.internal.R.integer.config_defaultUiModeType);
+ final String uiModeTypeStr = Configuration.getUiModeTypeString(uiModeType);
+ if (uiModeTypeStr != null) {
+ defaultXmlLocations.add(Environment.buildPath(Environment.getRootDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR,
+ String.format(DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT, uiModeTypeStr)));
}
- return config;
+ defaultXmlLocations.add(Environment.buildPath(Environment.getRootDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+
+ final File configFile = getFirstExistingFile(defaultXmlLocations);
+ if (configFile == null) {
+ // Display configuration files aren't required to exist.
+ return null;
+ }
+
+ DisplayConfiguration defaultConfig = null;
+
+ try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+ defaultConfig = XmlParser.read(in);
+ if (defaultConfig == null) {
+ Slog.i(TAG, "Default DisplayDeviceConfig file is null");
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
+ + configFile, e);
+ }
+
+ return defaultConfig;
+ }
+
+ private static File getFirstExistingFile(Collection<File> files) {
+ for (File file : files) {
+ if (file.exists() && file.isFile()) {
+ return file;
+ }
+ }
+ return null;
}
private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
@@ -316,9 +380,13 @@
return mRefreshRateLimitations;
}
+ public DensityMap getDensityMap() {
+ return mDensityMap;
+ }
+
@Override
public String toString() {
- String str = "DisplayDeviceConfig{"
+ return "DisplayDeviceConfig{"
+ "mLoadedFrom=" + mLoadedFrom
+ ", mBacklight=" + Arrays.toString(mBacklight)
+ ", mNits=" + Arrays.toString(mNits)
@@ -340,8 +408,8 @@
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ + ", mDensityMap= " + mDensityMap
+ "}";
- return str;
}
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
@@ -384,6 +452,7 @@
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
final DisplayConfiguration config = XmlParser.read(in);
if (config != null) {
+ loadDensityMap(config);
loadBrightnessDefaultFromDdcXml(config);
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
@@ -429,6 +498,35 @@
setProxSensorUnspecified();
}
+ private void copyUninitializedValuesFromSecondaryConfig(DisplayConfiguration defaultConfig) {
+ if (defaultConfig == null) {
+ return;
+ }
+
+ if (mDensityMap == null) {
+ loadDensityMap(defaultConfig);
+ }
+ }
+
+ private void loadDensityMap(DisplayConfiguration config) {
+ if (config.getDensityMap() == null) {
+ return;
+ }
+
+ final List<Density> entriesFromXml = config.getDensityMap().getDensity();
+
+ final DensityMap.Entry[] entries =
+ new DensityMap.Entry[entriesFromXml.size()];
+ for (int i = 0; i < entriesFromXml.size(); i++) {
+ final Density density = entriesFromXml.get(i);
+ entries[i] = new DensityMap.Entry(
+ density.getWidth().intValue(),
+ density.getHeight().intValue(),
+ density.getDensity().intValue());
+ }
+ mDensityMap = DensityMap.createByOwning(entries);
+ }
+
private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) {
// Default brightness values are stored in the displayDeviceConfig file,
// Or we fallback standard values if not.
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 40cee66..fd4cd8e 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -142,6 +142,15 @@
public static final int FLAG_OWN_DISPLAY_GROUP = 1 << 14;
/**
+ * Flag: Indicates that the display should always be unlocked. Only valid on virtual displays
+ * that aren't in the default display group.
+ * @see #FLAG_OWN_DISPLAY_GROUP
+ * @hide
+ */
+ public static final int FLAG_ALWAYS_UNLOCKED = 1 << 15;
+
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e4bed3d..77ab813 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -16,11 +16,13 @@
package com.android.server.display;
+import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.hardware.display.DisplayManager.EventsMask;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
@@ -94,6 +96,8 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.sysprop.DisplayProperties;
import android.text.TextUtils;
@@ -110,6 +114,7 @@
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -239,6 +244,10 @@
public final SparseArray<CallbackRecord> mCallbacks =
new SparseArray<CallbackRecord>();
+ /** All {@link DisplayWindowPolicyController}s indexed by {@link DisplayInfo#displayId}. */
+ final SparseArray<DisplayWindowPolicyController> mDisplayWindowPolicyController =
+ new SparseArray<>();
+
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -388,6 +397,7 @@
private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
private final Injector mInjector;
+ private final DeviceConfigInterface mDeviceConfig;
// The minimum brightness curve, which guarantess that any brightness curve that dips below it
// is rejected by the system.
@@ -438,6 +448,7 @@
DisplayManagerService(Context context, Injector injector) {
super(context);
mInjector = injector;
+ mDeviceConfig = mInjector.getDeviceConfig();
mContext = context;
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
@@ -1115,46 +1126,236 @@
}
}
- private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
- IMediaProjection projection, int callingUid, String packageName, Surface surface,
- int flags, VirtualDisplayConfig virtualDisplayConfig) {
- synchronized (mSyncRoot) {
- if (mVirtualDisplayAdapter == null) {
- Slog.w(TAG, "Rejecting request to create private virtual display "
- + "because the virtual display adapter is not available.");
- return -1;
+ private boolean validatePackageName(int uid, String packageName) {
+ if (packageName != null) {
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packageNames != null) {
+ for (String n : packageNames) {
+ if (n.equals(packageName)) {
+ return true;
+ }
+ }
}
-
- DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
- callback, projection, callingUid, packageName, surface, flags,
- virtualDisplayConfig);
- if (device == null) {
- return -1;
- }
-
- // DisplayDevice events are handled manually for Virtual Displays.
- // TODO: multi-display Fix this so that generic add/remove events are not handled in a
- // different code path for virtual displays. Currently this happens so that we can
- // return a valid display ID synchronously upon successful Virtual Display creation.
- // This code can run on any binder thread, while onDisplayDeviceAdded() callbacks are
- // called on the DisplayThread (which we don't want to wait for?).
- // One option would be to actually wait here on the binder thread
- // to be notified when the virtual display is created (or failed).
- mDisplayDeviceRepo.onDisplayDeviceEvent(device,
- DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
-
- final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
- if (display != null) {
- return display.getDisplayIdLocked();
- }
-
- // Something weird happened and the logical display was not created.
- Slog.w(TAG, "Rejecting request to create virtual display "
- + "because the logical display was not created.");
- mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
- mDisplayDeviceRepo.onDisplayDeviceEvent(device,
- DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
}
+ return false;
+ }
+
+ private boolean canProjectVideo(IMediaProjection projection) {
+ if (projection != null) {
+ try {
+ if (projection.canProjectVideo()) {
+ return true;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to query projection service for permissions", e);
+ }
+ }
+ if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {
+ return true;
+ }
+ return canProjectSecureVideo(projection);
+ }
+
+ private boolean canProjectSecureVideo(IMediaProjection projection) {
+ if (projection != null) {
+ try {
+ if (projection.canProjectSecureVideo()) {
+ return true;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to query projection service for permissions", e);
+ }
+ }
+ return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()");
+ }
+
+ private boolean checkCallingPermission(String permission, String func) {
+ if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid() + " requires " + permission;
+ Slog.w(TAG, msg);
+ return false;
+ }
+
+ private int createVirtualDisplayInternal(VirtualDisplayConfig virtualDisplayConfig,
+ IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
+ DisplayWindowPolicyController controller) {
+ final int callingUid = Binder.getCallingUid();
+ if (!validatePackageName(callingUid, packageName)) {
+ throw new SecurityException("packageName must match the calling uid");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("appToken must not be null");
+ }
+ if (virtualDisplayConfig == null) {
+ throw new IllegalArgumentException("virtualDisplayConfig must not be null");
+ }
+ final Surface surface = virtualDisplayConfig.getSurface();
+ int flags = virtualDisplayConfig.getFlags();
+
+ if (surface != null && surface.isSingleBuffered()) {
+ throw new IllegalArgumentException("Surface can't be single-buffered");
+ }
+
+ if ((flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
+ flags |= VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+
+ // Public displays can't be allowed to show content when locked.
+ if ((flags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
+ throw new IllegalArgumentException(
+ "Public display must not be marked as SHOW_WHEN_LOCKED_INSECURE");
+ }
+ }
+ if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
+ flags &= ~VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+ }
+ if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+ flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
+ }
+
+ if (projection != null) {
+ try {
+ if (!getProjectionService().isValidMediaProjection(projection)) {
+ throw new SecurityException("Invalid media projection");
+ }
+ flags = projection.applyVirtualDisplayFlags(flags);
+ } catch (RemoteException e) {
+ throw new SecurityException("unable to validate media projection or flags");
+ }
+ }
+
+ if (callingUid != Process.SYSTEM_UID
+ && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+ if (!canProjectVideo(projection)) {
+ throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
+ + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
+ + "MediaProjection token in order to create a screen sharing virtual "
+ + "display.");
+ }
+ }
+ if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
+ if (!canProjectSecureVideo(projection)) {
+ throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
+ + "or an appropriate MediaProjection token to create a "
+ + "secure virtual display.");
+ }
+ }
+
+ if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
+ if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
+ EventLog.writeEvent(0x534e4554, "162627132", callingUid,
+ "Attempt to create a trusted display without holding permission!");
+ throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+ + "create a trusted virtual display.");
+ }
+ }
+
+ if (callingUid != Process.SYSTEM_UID
+ && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
+ if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
+ throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+ + "create a virtual display which is not in the default DisplayGroup.");
+ }
+ }
+
+ if ((flags & VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED) != 0) {
+ if (callingUid != Process.SYSTEM_UID
+ && !checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY,
+ "createVirtualDisplay()")) {
+ throw new SecurityException(
+ "Requires ADD_ALWAYS_UNLOCKED_DISPLAY permission to "
+ + "create an always unlocked virtual display.");
+ }
+ boolean allowedByDeviceConfig = false;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ allowedByDeviceConfig = mDeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_ALLOW_ALWAYS_UNLOCKED_VIRTUAL_DISPLAYS,
+ false);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (!allowedByDeviceConfig) {
+ Slog.w(TAG, "Ignoring flag VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED "
+ + "because it is not allowed by DeviceConfig");
+ flags &= ~VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
+ }
+ }
+
+ if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+ flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+ }
+
+ // Sometimes users can have sensitive information in system decoration windows. An app
+ // could create a virtual display with system decorations support and read the user info
+ // from the surface.
+ // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ // to trusted virtual displays.
+ final int trustedDisplayWithSysDecorFlag =
+ (VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ | VIRTUAL_DISPLAY_FLAG_TRUSTED);
+ if ((flags & trustedDisplayWithSysDecorFlag)
+ == VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ && !checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
+ throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ return createVirtualDisplayLocked(callback, projection, callingUid, packageName,
+ surface, flags, virtualDisplayConfig, controller);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private int createVirtualDisplayLocked(IVirtualDisplayCallback callback,
+ IMediaProjection projection, int callingUid, String packageName, Surface surface,
+ int flags, VirtualDisplayConfig virtualDisplayConfig,
+ DisplayWindowPolicyController controller) {
+ if (mVirtualDisplayAdapter == null) {
+ Slog.w(TAG, "Rejecting request to create private virtual display "
+ + "because the virtual display adapter is not available.");
+ return -1;
+ }
+
+ DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
+ callback, projection, callingUid, packageName, surface, flags,
+ virtualDisplayConfig);
+ if (device == null) {
+ return -1;
+ }
+
+ // DisplayDevice events are handled manually for Virtual Displays.
+ // TODO: multi-display Fix this so that generic add/remove events are not handled in a
+ // different code path for virtual displays. Currently this happens so that we can
+ // return a valid display ID synchronously upon successful Virtual Display creation.
+ // This code can run on any binder thread, while onDisplayDeviceAdded() callbacks are
+ // called on the DisplayThread (which we don't want to wait for?).
+ // One option would be to actually wait here on the binder thread
+ // to be notified when the virtual display is created (or failed).
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device,
+ DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
+ if (display != null) {
+ if (controller != null) {
+ mDisplayWindowPolicyController.put(display.getDisplayIdLocked(), controller);
+ }
+ return display.getDisplayIdLocked();
+ }
+
+ // Something weird happened and the logical display was not created.
+ Slog.w(TAG, "Rejecting request to create virtual display "
+ + "because the logical display was not created.");
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device,
+ DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
return -1;
}
@@ -1188,6 +1389,10 @@
DisplayDevice device =
mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
if (device != null) {
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
+ if (display != null) {
+ mDisplayWindowPolicyController.delete(display.getDisplayIdLocked());
+ }
// TODO: multi-display - handle virtual displays the same as other display adapters.
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
@@ -2139,6 +2344,15 @@
}
pw.println();
mPersistentDataStore.dump(pw);
+
+ final int displayWindowPolicyControllerCount = mDisplayWindowPolicyController.size();
+ pw.println();
+ pw.println("Display Window Policy Controllers: size="
+ + displayWindowPolicyControllerCount);
+ for (int i = 0; i < displayWindowPolicyControllerCount; i++) {
+ pw.print("Display " + mDisplayWindowPolicyController.keyAt(i) + ":");
+ mDisplayWindowPolicyController.valueAt(i).dump(" ", pw);
+ }
}
pw.println();
mDisplayModeDirector.dump(pw);
@@ -2183,6 +2397,11 @@
return DisplayProperties
.debug_allow_non_native_refresh_rate_override().orElse(false);
}
+
+ @NonNull
+ public DeviceConfigInterface getDeviceConfig() {
+ return DeviceConfigInterface.REAL;
+ }
}
@VisibleForTesting
@@ -2704,109 +2923,8 @@
@Override // Binder call
public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) {
- final int callingUid = Binder.getCallingUid();
- if (!validatePackageName(callingUid, packageName)) {
- throw new SecurityException("packageName must match the calling uid");
- }
- if (callback == null) {
- throw new IllegalArgumentException("appToken must not be null");
- }
- if (virtualDisplayConfig == null) {
- throw new IllegalArgumentException("virtualDisplayConfig must not be null");
- }
- final Surface surface = virtualDisplayConfig.getSurface();
- int flags = virtualDisplayConfig.getFlags();
-
- if (surface != null && surface.isSingleBuffered()) {
- throw new IllegalArgumentException("Surface can't be single-buffered");
- }
-
- if ((flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
- flags |= VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
-
- // Public displays can't be allowed to show content when locked.
- if ((flags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
- throw new IllegalArgumentException(
- "Public display must not be marked as SHOW_WHEN_LOCKED_INSECURE");
- }
- }
- if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
- flags &= ~VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
- }
- if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
- flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
- }
-
- if (projection != null) {
- try {
- if (!getProjectionService().isValidMediaProjection(projection)) {
- throw new SecurityException("Invalid media projection");
- }
- flags = projection.applyVirtualDisplayFlags(flags);
- } catch (RemoteException e) {
- throw new SecurityException("unable to validate media projection or flags");
- }
- }
-
- if (callingUid != Process.SYSTEM_UID &&
- (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
- if (!canProjectVideo(projection)) {
- throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
- + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
- + "MediaProjection token in order to create a screen sharing virtual "
- + "display.");
- }
- }
- if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
- if (!canProjectSecureVideo(projection)) {
- throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
- + "or an appropriate MediaProjection token to create a "
- + "secure virtual display.");
- }
- }
-
- if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
- if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
- EventLog.writeEvent(0x534e4554, "162627132", callingUid,
- "Attempt to create a trusted display without holding permission!");
- throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
- + "create a trusted virtual display.");
- }
- }
-
- if (callingUid != Process.SYSTEM_UID
- && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
- if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
- throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
- + "create a virtual display which is not in the default DisplayGroup.");
- }
- }
-
- if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
- flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
- }
-
- // Sometimes users can have sensitive information in system decoration windows. An app
- // could create a virtual display with system decorations support and read the user info
- // from the surface.
- // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
- // to trusted virtual displays.
- final int trustedDisplayWithSysDecorFlag =
- (VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
- | VIRTUAL_DISPLAY_FLAG_TRUSTED);
- if ((flags & trustedDisplayWithSysDecorFlag)
- == VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
- && !checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
- throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- return createVirtualDisplayInternal(callback, projection, callingUid, packageName,
- surface, flags, virtualDisplayConfig);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
+ packageName, null /* controller */);
}
@Override // Binder call
@@ -3236,60 +3354,6 @@
Binder.restoreCallingIdentity(token);
}
}
-
- private boolean validatePackageName(int uid, String packageName) {
- if (packageName != null) {
- String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
- if (packageNames != null) {
- for (String n : packageNames) {
- if (n.equals(packageName)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- private boolean canProjectVideo(IMediaProjection projection) {
- if (projection != null) {
- try {
- if (projection.canProjectVideo()) {
- return true;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to query projection service for permissions", e);
- }
- }
- if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {
- return true;
- }
- return canProjectSecureVideo(projection);
- }
-
- private boolean canProjectSecureVideo(IMediaProjection projection) {
- if (projection != null) {
- try {
- if (projection.canProjectSecureVideo()){
- return true;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to query projection service for permissions", e);
- }
- }
- return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()");
- }
-
- private boolean checkCallingPermission(String permission, String func) {
- if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid() + " requires " + permission;
- Slog.w(TAG, msg);
- return false;
- }
-
}
private static boolean isValidBrightness(float brightness) {
@@ -3624,6 +3688,21 @@
public void onEarlyInteractivityChange(boolean interactive) {
mLogicalDisplayMapper.onEarlyInteractivityChange(interactive);
}
+
+ @Override
+ public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
+ IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
+ DisplayWindowPolicyController controller) {
+ return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
+ packageName, controller);
+ }
+
+ @Override
+ public DisplayWindowPolicyController getDisplayWindowPolicyController(int displayId) {
+ synchronized (mSyncRoot) {
+ return mDisplayWindowPolicyController.get(displayId);
+ }
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 5f79f72..7f78cac 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.TimeUtils;
import android.view.Display;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.display.BrightnessSynchronizer;
@@ -386,8 +387,17 @@
private Sensor mLightSensor;
// The mapper between ambient lux, display backlight values, and display brightness.
+ // This mapper holds the current one that is being used. We will switch between the idle
+ // mapper and active mapper here.
@Nullable
- private BrightnessMappingStrategy mBrightnessMapper;
+ private BrightnessMappingStrategy mCurrentBrightnessMapper;
+
+ // Mapper used for active (normal) screen brightness mode
+ @Nullable
+ private BrightnessMappingStrategy mInteractiveModeBrightnessMapper;
+ // Mapper used for idle screen brightness mode
+ @Nullable
+ private BrightnessMappingStrategy mIdleModeBrightnessMapper;
// The current brightness configuration.
@Nullable
@@ -408,7 +418,7 @@
// The temporary screen brightness. Typically set when a user is interacting with the
// brightness slider but hasn't settled on a choice yet. Set to
- // PowerManager.BRIGHNTESS_INVALID_FLOAT when there's no temporary brightness set.
+ // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary brightness set.
private float mTemporaryScreenBrightness;
// The current screen brightness while in VR mode.
@@ -600,7 +610,7 @@
}
private void handleRbcChanged(boolean strengthChanged, boolean justActivated) {
- if (mBrightnessMapper == null) {
+ if (mCurrentBrightnessMapper == null) {
Log.w(TAG, "No brightness mapping available to recalculate splines");
return;
}
@@ -609,7 +619,8 @@
for (int i = 0; i < mNitsRange.length; i++) {
adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
}
- mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
+ mCurrentBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(),
+ adjustedNits);
mPendingRbcOnOrChanged = strengthChanged || justActivated;
@@ -867,9 +878,17 @@
return;
}
- mBrightnessMapper = BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig);
+ final boolean isIdleScreenBrightnessEnabled = resources.getBoolean(
+ R.bool.config_enableIdleScreenBrightnessMode);
+ mInteractiveModeBrightnessMapper = BrightnessMappingStrategy.create(resources,
+ mDisplayDeviceConfig);
+ if (isIdleScreenBrightnessEnabled) {
+ mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources,
+ mDisplayDeviceConfig);
+ }
+ mCurrentBrightnessMapper = mInteractiveModeBrightnessMapper;
- if (mBrightnessMapper != null) {
+ if (mCurrentBrightnessMapper != null) {
final float dozeScaleFactor = resources.getFraction(
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
1, 1);
@@ -920,7 +939,7 @@
mAutomaticBrightnessController.stop();
}
mAutomaticBrightnessController = new AutomaticBrightnessController(this,
- handler.getLooper(), mSensorManager, mLightSensor, mBrightnessMapper,
+ handler.getLooper(), mSensorManager, mLightSensor, mCurrentBrightnessMapper,
lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
@@ -2143,8 +2162,8 @@
}
private float convertToNits(float brightness) {
- if (mBrightnessMapper != null) {
- return mBrightnessMapper.convertToNits(brightness);
+ if (mCurrentBrightnessMapper != null) {
+ return mCurrentBrightnessMapper.convertToNits(brightness);
} else {
return -1.0f;
}
@@ -2296,6 +2315,11 @@
pw.println(" mReportedToPolicy=" +
reportedToPolicyToString(mReportedScreenStateToPolicy));
+ if (mIdleModeBrightnessMapper != null) {
+ pw.println(" mIdleModeBrightnessMapper= ");
+ mIdleModeBrightnessMapper.dump(pw);
+ }
+
if (mScreenBrightnessRampAnimator != null) {
pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" +
mScreenBrightnessRampAnimator.isAnimating());
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b6d13e0..300f59e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -426,6 +426,15 @@
: mDefaultModeId;
}
+ private int getLogicalDensity() {
+ DensityMap densityMap = getDisplayDeviceConfig().getDensityMap();
+ if (densityMap == null) {
+ return (int) (mStaticDisplayInfo.density * 160 + 0.5);
+ }
+
+ return densityMap.getDensityForResolution(mInfo.width, mInfo.height);
+ }
+
private void loadDisplayDeviceConfig() {
// Load display device config
final Context context = getOverlayContext();
@@ -591,7 +600,7 @@
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
mInfo.address = physicalAddress;
- mInfo.densityDpi = (int) (mStaticDisplayInfo.density * 160 + 0.5f);
+ mInfo.densityDpi = getLogicalDensity();
mInfo.xDpi = mActiveSfDisplayMode.xDpi;
mInfo.yDpi = mActiveSfDisplayMode.yDpi;
mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo;
@@ -1029,7 +1038,7 @@
for (int i = 0; i < mSupportedModes.size(); i++) {
pw.println(" " + mSupportedModes.valueAt(i));
}
- pw.println("mSupportedColorModes=" + mSupportedColorModes.toString());
+ pw.println("mSupportedColorModes=" + mSupportedColorModes);
pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 48eea89..4d1367a3 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -378,6 +378,9 @@
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_OWN_DISPLAY_GROUP;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_ALWAYS_UNLOCKED;
+ }
Rect maskingInsets = getMaskingInsets(deviceInfo);
int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index a592192..797c3fb 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
@@ -28,6 +29,7 @@
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+import static com.android.server.display.DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED;
import static com.android.server.display.DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
@@ -453,6 +455,10 @@
if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
mInfo.flags |= FLAG_TRUSTED;
}
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED) != 0
+ && (mInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) {
+ mInfo.flags |= FLAG_ALWAYS_UNLOCKED;
+ }
mInfo.type = Display.TYPE_VIRTUAL;
mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 1fa6241..7e71589 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -265,13 +265,20 @@
// to request Short Audio Descriptor. Since ARC and SAM are independent,
// we can turn on ARC anyways when audio system device just boots up.
initArcOnFromAvr();
- int systemAudioControlOnPowerOnProp =
- SystemProperties.getInt(
- PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
- ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON);
- boolean lastSystemAudioControlStatus =
- SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
- systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
+
+ // This prevents turning on of System Audio Mode during a quiescent boot. If the quiescent
+ // boot is exited just after this check, this code will be executed only at the next
+ // wake-up.
+ if (!mService.isScreenOff()) {
+ int systemAudioControlOnPowerOnProp =
+ SystemProperties.getInt(
+ PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
+ ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON);
+ boolean lastSystemAudioControlStatus =
+ SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
+ systemAudioControlOnPowerOn(
+ systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
+ }
mService.getHdmiCecNetwork().clearDeviceList();
launchDeviceDiscovery();
startQueuedActions();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d6ac25a..b23395f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -21,6 +21,7 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
@@ -47,6 +48,10 @@
private static final boolean SET_MENU_LANGUAGE =
HdmiProperties.set_menu_language_enabled().orElse(false);
+ // How long to wait after hotplug out before possibly going to Standby.
+ @VisibleForTesting
+ static final long STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS = 30_000;
+
// Used to keep the device awake while it is the active source. For devices that
// cannot wake up via CEC commands, this address the inconvenience of having to
// turn them on. True by default, and can be disabled (i.e. device can go to sleep
@@ -55,6 +60,9 @@
// Lazily initialized - should call getWakeLock() to get the instance.
private ActiveWakeLock mWakeLock;
+ // Handler for queueing a delayed Standby runnable after hotplug out.
+ private Handler mDelayedStandbyHandler;
+
// Determines what action should be taken upon receiving Routing Control messages.
@VisibleForTesting
protected HdmiProperties.playback_device_action_on_routing_control_values
@@ -64,6 +72,8 @@
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
+
+ mDelayedStandbyHandler = new Handler(service.getServiceLooper());
}
@Override
@@ -195,9 +205,33 @@
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
mCecMessageCache.flushAll();
- // We'll not invalidate the active source on the hotplug event to pass CETC 11.2.2-2 ~ 3.
- if (!connected) {
+
+ if (connected) {
+ mDelayedStandbyHandler.removeCallbacksAndMessages(null);
+ } else {
+ // We'll not invalidate the active source on the hotplug event to pass CETC 11.2.2-2 ~ 3
getWakeLock().release();
+
+ mDelayedStandbyHandler.removeCallbacksAndMessages(null);
+ mDelayedStandbyHandler.postDelayed(new DelayedStandbyRunnable(),
+ STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS);
+ }
+ }
+
+ /**
+ * Runnable for going to Standby if the device has been inactive for a certain amount of time.
+ * Posts a new instance of itself as a delayed message if the device was active.
+ */
+ private class DelayedStandbyRunnable implements Runnable {
+ @Override
+ public void run() {
+ if (mService.getPowerManagerInternal().wasDeviceIdleFor(
+ STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS)) {
+ mService.standby();
+ } else {
+ mDelayedStandbyHandler.postDelayed(new DelayedStandbyRunnable(),
+ STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS);
+ }
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 69f7af2..6dd9aa0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -37,6 +37,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
@@ -81,6 +82,7 @@
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -408,8 +410,14 @@
private PowerManagerWrapper mPowerManager;
@Nullable
+ private PowerManagerInternalWrapper mPowerManagerInternal;
+
+ @Nullable
private Looper mIoLooper;
+ @Nullable
+ private DisplayManager mDisplayManager;
+
@HdmiControlManager.HdmiCecVersion
private int mCecVersion;
@@ -676,6 +684,11 @@
}, mServiceThreadExecutor);
}
+ /** Returns true if the device screen is off */
+ boolean isScreenOff() {
+ return mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_OFF;
+ }
+
private void bootCompleted() {
// on boot, if device is interactive, set HDMI CEC state as powered on as well
if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
@@ -731,9 +744,11 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ mDisplayManager = getContext().getSystemService(DisplayManager.class);
mTvInputManager = (TvInputManager) getContext().getSystemService(
Context.TV_INPUT_SERVICE);
mPowerManager = new PowerManagerWrapper(getContext());
+ mPowerManagerInternal = new PowerManagerInternalWrapper();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
runOnServiceThread(this::bootCompleted);
}
@@ -758,10 +773,19 @@
mPowerManager = powerManager;
}
+ @VisibleForTesting
+ void setPowerManagerInternal(PowerManagerInternalWrapper powerManagerInternal) {
+ mPowerManagerInternal = powerManagerInternal;
+ }
+
PowerManagerWrapper getPowerManager() {
return mPowerManager;
}
+ PowerManagerInternalWrapper getPowerManagerInternal() {
+ return mPowerManagerInternal;
+ }
+
/**
* Called when the initialization of local devices is complete.
*/
@@ -3239,18 +3263,19 @@
assertRunOnServiceThread();
Slog.v(TAG, "onPendingActionsCleared");
- if (!mPowerStatusController.isPowerStatusTransientToStandby()) {
- return;
+ if (mPowerStatusController.isPowerStatusTransientToStandby()) {
+ mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
+ for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
+ device.onStandby(mStandbyMessageReceived, standbyAction);
+ }
+ if (!isAudioSystemDevice()) {
+ mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+ }
}
- mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
- for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
- device.onStandby(mStandbyMessageReceived, standbyAction);
- }
+
+ // Always reset this flag to set up for the next standby
mStandbyMessageReceived = false;
- if (!isAudioSystemDevice()) {
- mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
- mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
- }
}
private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
diff --git a/services/core/java/com/android/server/hdmi/PowerManagerInternalWrapper.java b/services/core/java/com/android/server/hdmi/PowerManagerInternalWrapper.java
new file mode 100644
index 0000000..59671a8
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/PowerManagerInternalWrapper.java
@@ -0,0 +1,41 @@
+/*
+ * 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.server.hdmi;
+
+import android.os.PowerManagerInternal;
+
+import com.android.server.LocalServices;
+
+/**
+ * Abstraction around {@link PowerManagerInternal} to allow faking it in tests.
+ */
+public class PowerManagerInternalWrapper {
+ private static final String TAG = "PowerManagerInternalWrapper";
+
+ private PowerManagerInternal mPowerManagerInternal;
+
+ public PowerManagerInternalWrapper() {
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ }
+
+ /**
+ * Wraps {@link PowerManagerInternal#wasDeviceIdleFor(long)}
+ */
+ public boolean wasDeviceIdleFor(long ms) {
+ return mPowerManagerInternal.wasDeviceIdleFor(ms);
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c879e3d..9499e51 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2790,7 +2790,12 @@
final long ident = Binder.clearCallingIdentity();
try {
if (!mCurPerceptible) {
- vis &= ~InputMethodService.IME_VISIBLE;
+ if ((vis & InputMethodService.IME_VISIBLE) != 0) {
+ vis &= ~InputMethodService.IME_VISIBLE;
+ vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
+ }
+ } else {
+ vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
}
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index b8bb399..f70ad05 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -224,7 +224,7 @@
public Context getSettingsContext(int displayId) {
if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
final Context systemUiContext = ActivityThread.currentActivityThread()
- .createSystemUiContext(displayId);
+ .getSystemUiContext(displayId);
final Context windowContext = systemUiContext.createWindowContext(
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
mSettingsContext = new ContextThemeWrapper(
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index ffeaad1..457c2fd 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -154,10 +154,10 @@
final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName,
userId);
- boolean isSuccess = updater.setLocales(locales).commit();
+ boolean isConfigChanged = updater.setLocales(locales).commit();
//We want to send the broadcasts only if config was actually updated on commit.
- if (isSuccess) {
+ if (isConfigChanged) {
notifyAppWhoseLocaleChanged(appPackageName, userId, locales);
notifyInstallerOfAppWhoseLocaleChanged(appPackageName, userId, locales);
notifyRegisteredReceivers(appPackageName, userId, locales);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index b676f28..efd3037 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -30,6 +30,7 @@
import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.content.Intent;
+import android.hardware.contexthub.HostEndpointInfo;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubManager;
import android.hardware.location.ContextHubTransaction;
@@ -42,6 +43,7 @@
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
@@ -329,6 +331,15 @@
mAppOpsManager = context.getSystemService(AppOpsManager.class);
startMonitoringOpChanges();
+
+ HostEndpointInfo info = new HostEndpointInfo();
+ info.hostEndpointId = (char) mHostEndPointId;
+ info.packageName = mPackage;
+ info.attributionTag = mAttributionTag;
+ info.type = (mUid == Process.SYSTEM_UID)
+ ? HostEndpointInfo.Type.TYPE_FRAMEWORK
+ : HostEndpointInfo.Type.TYPE_APP;
+ mContextHubProxy.onHostEndpointConnected(info);
}
/* package */ ContextHubClientBroker(
@@ -862,6 +873,8 @@
mRegistered = false;
}
mAppOpsManager.stopWatchingMode(this);
+
+ mContextHubProxy.onHostEndpointDisconnected(mHostEndPointId);
}
private String authStateToString(@ContextHubManager.AuthorizationState int state) {
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 74630d1..9078f3f 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.contexthub.HostEndpointInfo;
import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.TransactionResult;
@@ -239,6 +240,20 @@
public abstract void onMicrophoneSettingChanged(boolean enabled);
/**
+ * Invoked whenever a host client connects with the framework.
+ *
+ * @param info The host endpoint info.
+ */
+ public void onHostEndpointConnected(HostEndpointInfo info) {}
+
+ /**
+ * Invoked whenever a host client disconnects from the framework.
+ *
+ * @param hostEndpointId The ID of the host endpoint that disconnected.
+ */
+ public void onHostEndpointDisconnected(short hostEndpointId) {}
+
+ /**
* Sends a message to the Context Hub.
*
* @param hostEndpointId The host endpoint ID of the sender.
@@ -407,6 +422,24 @@
onSettingChanged(android.hardware.contexthub.Setting.WIFI_SCANNING, enabled);
}
+ @Override
+ public void onHostEndpointConnected(HostEndpointInfo info) {
+ try {
+ mHub.onHostEndpointConnected(info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in onHostEndpointConnected");
+ }
+ }
+
+ @Override
+ public void onHostEndpointDisconnected(short hostEndpointId) {
+ try {
+ mHub.onHostEndpointDisconnected((char) hostEndpointId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in onHostEndpointDisconnected");
+ }
+ }
+
@ContextHubTransaction.Result
public int sendMessageToContextHub(
short hostEndpointId, int contextHubId, NanoAppMessage message)
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index 7db234a..5fe7710 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -71,7 +71,8 @@
private static final String CONFIG_GPS_LOCK = "GPS_LOCK";
private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC";
public static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS";
-
+ public static final String CONFIG_ENABLE_PSDS_PERIODIC_DOWNLOAD =
+ "ENABLE_PSDS_PERIODIC_DOWNLOAD";
// Limit on NI emergency mode time extension after emergency sessions ends
private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum
@@ -194,6 +195,13 @@
}
/**
+ * Returns true if PSDS periodic download is enabled, false otherwise.
+ */
+ boolean isPsdsPeriodicDownloadEnabled() {
+ return getBooleanConfig(CONFIG_ENABLE_PSDS_PERIODIC_DOWNLOAD, false);
+ }
+
+ /**
* Updates the GNSS HAL satellite denylist.
*/
void setSatelliteBlocklist(int[] constellations, int[] svids) {
@@ -374,6 +382,14 @@
}
}
+ private boolean getBooleanConfig(String configParameter, boolean defaultValue) {
+ String valueString = mProperties.getProperty(configParameter);
+ if (TextUtils.isEmpty(valueString)) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(valueString);
+ }
+
private static boolean isConfigEsExtensionSecSupported(
HalInterfaceVersion gnssConfiguartionIfaceVersion) {
// ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal
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 6c1df7f..f114184 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -16,6 +16,8 @@
package com.android.server.location.gnss;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.location.provider.ProviderProperties.ACCURACY_FINE;
import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
@@ -55,6 +57,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.location.GnssCapabilities;
import android.location.GnssStatus;
@@ -142,6 +145,9 @@
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
+ // PSDS stands for Predicted Satellite Data Service
+ private static final int DOWNLOAD_PSDS_DATA = 6;
+
// TCP/IP constants.
// Valid TCP/UDP port range is (0, 65535].
private static final int TCP_MIN_PORT = 0;
@@ -651,6 +657,14 @@
mPsdsBackOff.reset();
}
});
+ PackageManager pm = mContext.getPackageManager();
+ if (pm != null && pm.hasSystemFeature(FEATURE_WATCH)
+ && mGnssConfiguration.isPsdsPeriodicDownloadEnabled()) {
+ if (DEBUG) Log.d(TAG, "scheduling next Psds download");
+ mHandler.removeMessages(DOWNLOAD_PSDS_DATA);
+ mHandler.sendEmptyMessageDelayed(DOWNLOAD_PSDS_DATA,
+ GnssPsdsDownloader.PSDS_INTERVAL);
+ }
} else {
// Try download PSDS data again later according to backoff time.
// Since this is delayed and not urgent, we do not hold a wake lock here.
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 8460d67..1781588 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -52,8 +52,6 @@
private class GnssMeasurementListenerRegistration extends GnssListenerRegistration {
- private static final String GNSS_MEASUREMENTS_BUCKET = "gnss_measurement";
-
protected GnssMeasurementListenerRegistration(
@Nullable GnssMeasurementRequest request,
CallerIdentity callerIdentity,
@@ -70,15 +68,13 @@
@Nullable
@Override
protected void onActive() {
- mLocationAttributionHelper.reportHighPowerLocationStart(
- getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+ mLocationAttributionHelper.reportHighPowerLocationStart(getIdentity());
}
@Nullable
@Override
protected void onInactive() {
- mLocationAttributionHelper.reportHighPowerLocationStop(
- getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+ mLocationAttributionHelper.reportHighPowerLocationStop(getIdentity());
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java b/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java
index 443a6c0..dce9a12 100644
--- a/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java
+++ b/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java
@@ -38,6 +38,10 @@
*/
class GnssPsdsDownloader {
+ // how often to request PSDS download, in milliseconds
+ // current setting 24 hours
+ static final long PSDS_INTERVAL = 24 * 60 * 60 * 1000;
+
private static final String TAG = "GnssPsdsDownloader";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final long MAXIMUM_CONTENT_LENGTH_BYTES = 1000000; // 1MB.
diff --git a/services/core/java/com/android/server/location/gnss/gps_debug.conf b/services/core/java/com/android/server/location/gnss/gps_debug.conf
index 34ce96f..90daf8c 100644
--- a/services/core/java/com/android/server/location/gnss/gps_debug.conf
+++ b/services/core/java/com/android/server/location/gnss/gps_debug.conf
@@ -50,3 +50,12 @@
# Set bit 0x2 if NI GPS functionalities are to be locked
# default - non is locked for backward compatibility
#GPS_LOCK = 0
+
+################################
+##### PSDS download settings #####
+################################
+# For wear devices only.
+# Enable periodic PSDS download once a day.
+# true: Enable periodic PSDS download
+# false: Disable periodic PSDS download
+#ENABLE_PSDS_PERIODIC_DOWNLOAD=false
diff --git a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
index 5cb360b..4838752 100644
--- a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
@@ -24,55 +24,23 @@
import android.location.util.identity.CallerIdentity;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
/**
* Helps manage appop monitoring for multiple location clients.
*/
public class LocationAttributionHelper {
- private static class BucketKey {
- private final String mBucket;
- private final Object mKey;
-
- private BucketKey(String bucket, Object key) {
- mBucket = Objects.requireNonNull(bucket);
- mKey = Objects.requireNonNull(key);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- BucketKey that = (BucketKey) o;
- return mBucket.equals(that.mBucket)
- && mKey.equals(that.mKey);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mBucket, mKey);
- }
- }
-
private final AppOpsHelper mAppOpsHelper;
@GuardedBy("this")
- private final Map<CallerIdentity, Set<BucketKey>> mAttributions;
+ private final Map<CallerIdentity, Integer> mAttributions;
@GuardedBy("this")
- private final Map<CallerIdentity, Set<BucketKey>> mHighPowerAttributions;
+ private final Map<CallerIdentity, Integer> mHighPowerAttributions;
public LocationAttributionHelper(AppOpsHelper appOpsHelper) {
mAppOpsHelper = appOpsHelper;
@@ -84,15 +52,16 @@
/**
* Report normal location usage for the given caller in the given bucket, with a unique key.
*/
- public synchronized void reportLocationStart(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mAttributions.computeIfAbsent(identity,
- i -> new ArraySet<>());
- boolean empty = keySet.isEmpty();
- if (keySet.add(new BucketKey(bucket, key)) && empty) {
- if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
- mAttributions.remove(identity);
+ public synchronized void reportLocationStart(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mAttributions.getOrDefault(identity, 0);
+ if (count == 0) {
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
+ mAttributions.put(identity, 1);
}
+ } else {
+ mAttributions.put(identity, count + 1);
}
}
@@ -100,13 +69,15 @@
* Report normal location usage has stopped for the given caller in the given bucket, with a
* unique key.
*/
- public synchronized void reportLocationStop(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mAttributions.get(identity);
- if (keySet != null && keySet.remove(new BucketKey(bucket, key))
- && keySet.isEmpty()) {
+ public synchronized void reportLocationStop(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mAttributions.getOrDefault(identity, 0);
+ if (count == 1) {
mAttributions.remove(identity);
mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
+ } else if (count > 1) {
+ mAttributions.put(identity, count - 1);
}
}
@@ -114,19 +85,19 @@
* Report high power location usage for the given caller in the given bucket, with a unique
* key.
*/
- public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mHighPowerAttributions.computeIfAbsent(identity,
- i -> new ArraySet<>());
- boolean empty = keySet.isEmpty();
- if (keySet.add(new BucketKey(bucket, key)) && empty) {
- if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
- if (D) {
- Log.v(TAG, "starting high power location attribution for " + identity);
- }
- } else {
- mHighPowerAttributions.remove(identity);
+ public synchronized void reportHighPowerLocationStart(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mHighPowerAttributions.getOrDefault(identity, 0);
+ if (count == 0) {
+ if (D) {
+ Log.v(TAG, "starting high power location attribution for " + identity);
}
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+ mHighPowerAttributions.put(identity, 1);
+ }
+ } else {
+ mHighPowerAttributions.put(identity, count + 1);
}
}
@@ -134,16 +105,18 @@
* Report high power location usage has stopped for the given caller in the given bucket,
* with a unique key.
*/
- public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mHighPowerAttributions.get(identity);
- if (keySet != null && keySet.remove(new BucketKey(bucket, key))
- && keySet.isEmpty()) {
+ public synchronized void reportHighPowerLocationStop(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mHighPowerAttributions.getOrDefault(identity, 0);
+ if (count == 1) {
if (D) {
Log.v(TAG, "stopping high power location attribution for " + identity);
}
mHighPowerAttributions.remove(identity);
mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
+ } else if (count > 1) {
+ mHighPowerAttributions.put(identity, count - 1);
}
}
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 17efeb3..0ce24dd 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -398,7 +398,7 @@
EVENT_LOG.logProviderClientActive(mName, getIdentity());
if (!getRequest().isHiddenFromAppOps()) {
- mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
+ mLocationAttributionHelper.reportLocationStart(getIdentity());
}
onHighPowerUsageChanged();
@@ -413,7 +413,7 @@
onHighPowerUsageChanged();
if (!getRequest().isHiddenFromAppOps()) {
- mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
+ mLocationAttributionHelper.reportLocationStop(getIdentity());
}
onProviderListenerInactive();
@@ -488,10 +488,10 @@
if (!getRequest().isHiddenFromAppOps()) {
if (mIsUsingHighPower) {
mLocationAttributionHelper.reportHighPowerLocationStart(
- getIdentity(), getName(), getKey());
+ getIdentity());
} else {
mLocationAttributionHelper.reportHighPowerLocationStop(
- getIdentity(), getName(), getKey());
+ getIdentity());
}
}
}
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index df1eb6d..d17dbde 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -322,8 +322,11 @@
gateway = InetAddresses.parseNumericAddress(in.readUTF());
}
// If the destination is a default IPv4 route, use the gateway
- // address unless already set.
- if (dest.getAddress() instanceof Inet4Address
+ // address unless already set. If there is no destination, assume
+ // it is default route and use the gateway address in all cases.
+ if (dest == null) {
+ gatewayAddress = gateway;
+ } else if (dest.getAddress() instanceof Inet4Address
&& dest.getPrefixLength() == 0 && gatewayAddress == null) {
gatewayAddress = gateway;
} else {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 64f72c5..bf50db8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4739,6 +4739,8 @@
? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
+ newAllowedReasons |= (uidBlockedState.allowedReasons
+ & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
& BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index f4b72a1..c876d41 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -150,6 +150,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.BinderUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -2097,14 +2098,18 @@
@Override
public void notifyAlertReached() throws RemoteException {
- mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
+ // This binder object can only have been obtained by a process that holds
+ // NETWORK_STATS_PROVIDER. Thus, no additional permission check is required.
+ BinderUtils.withCleanCallingIdentity(() ->
+ mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
}
@Override
public void notifyWarningOrLimitReached() {
Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
- LocalServices.getService(NetworkPolicyManagerInternal.class)
- .onStatsProviderWarningOrLimitReached(mTag);
+ BinderUtils.withCleanCallingIdentity(() ->
+ LocalServices.getService(NetworkPolicyManagerInternal.class)
+ .onStatsProviderWarningOrLimitReached(mTag));
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bde5543..99fdb2d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6822,13 +6822,11 @@
// blocked apps
- boolean isMediaNotification = n.isMediaNotification()
- && n.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
synchronized (mNotificationLock) {
isBlocked |= isRecordBlockedLocked(r);
}
- if (isBlocked && !isMediaNotification) {
+ if (isBlocked && !n.isMediaNotification()) {
if (DBG) {
Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
+ " by user request.");
@@ -7217,10 +7215,8 @@
final StatusBarNotification n = r.getSbn();
final Notification notification = n.getNotification();
- boolean isMediaNotification = notification.isMediaNotification()
- && notification.extras.getParcelable(
- Notification.EXTRA_MEDIA_SESSION) != null;
- if (!isMediaNotification && (appBanned || isRecordBlockedLocked(r))) {
+ if (!notification.isMediaNotification()
+ && (appBanned || isRecordBlockedLocked(r))) {
mUsageStats.registerBlocked(r);
if (DBG) {
Slog.e(TAG, "Suppressing notification from package " + pkg);
@@ -9643,7 +9639,7 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- List<String> associations = mCompanionManager.getAssociations(
+ List<?> associations = mCompanionManager.getAssociations(
info.component.getPackageName(), info.userid);
if (!ArrayUtils.isEmpty(associations)) {
return true;
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index 5199ef6..be5f219 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -89,7 +89,7 @@
*/
public void vibrate(VibrationEffect effect, AudioAttributes attrs, String reason) {
mVibrator.vibrate(Process.SYSTEM_UID, PackageManagerService.PLATFORM_PACKAGE_NAME,
- effect, reason, new VibrationAttributes.Builder(attrs, effect).build());
+ effect, reason, new VibrationAttributes.Builder(attrs).build());
}
/** Stop all notification vibrations (ringtone, alarm, notification usages). */
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 53c2802..6f54625 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -500,16 +500,10 @@
forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
}
}
- final StateProvider stateProvider = new StateProvider() {
- // TODO: This lock and its handling should be owned by AppsFilter
- private final Object mLock = new Object();
-
- @Override
- public void runWithState(CurrentStateCallback command) {
- synchronized (mLock) {
- command.currentState(pms.getPackageStates(),
- injector.getUserManagerInternal().getUserInfos());
- }
+ final StateProvider stateProvider = command -> {
+ synchronized (injector.getLock()) {
+ command.currentState(injector.getSettings().getPackagesLocked().untrackedStorage(),
+ injector.getUserManagerInternal().getUserInfos());
}
};
AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig,
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index dca8654..6ec3405 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -318,7 +318,7 @@
}
@Nullable
- List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> queryActivities(Intent intent, String resolvedType, long flags,
int userId) {
synchronized (mLock) {
return mActivities.queryIntent(intent, resolvedType, flags, userId);
@@ -326,7 +326,7 @@
}
@Nullable
- List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> queryActivities(Intent intent, String resolvedType, long flags,
List<ParsedActivity> activities, int userId) {
synchronized (mLock) {
return mActivities.queryIntentForPackage(
@@ -335,14 +335,14 @@
}
@Nullable
- List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags, int userId) {
+ List<ResolveInfo> queryProviders(Intent intent, String resolvedType, long flags, int userId) {
synchronized (mLock) {
return mProviders.queryIntent(intent, resolvedType, flags, userId);
}
}
@Nullable
- List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> queryProviders(Intent intent, String resolvedType, long flags,
List<ParsedProvider> providers, int userId) {
synchronized (mLock) {
return mProviders.queryIntentForPackage(intent, resolvedType, flags, providers, userId);
@@ -350,7 +350,7 @@
}
@Nullable
- List<ProviderInfo> queryProviders(String processName, String metaDataKey, int uid, int flags,
+ List<ProviderInfo> queryProviders(String processName, String metaDataKey, int uid, long flags,
int userId) {
if (!sUserManager.exists(userId)) {
return null;
@@ -409,7 +409,7 @@
}
@Nullable
- ProviderInfo queryProvider(String authority, int flags, int userId) {
+ ProviderInfo queryProvider(String authority, long flags, int userId) {
synchronized (mLock) {
final ParsedProvider p = mProvidersByAuthority.get(authority);
if (p == null) {
@@ -480,14 +480,14 @@
}
@Nullable
- List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags, int userId) {
+ List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, long flags, int userId) {
synchronized (mLock) {
return mReceivers.queryIntent(intent, resolvedType, flags, userId);
}
}
@Nullable
- List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, long flags,
List<ParsedActivity> receivers, int userId) {
synchronized (mLock) {
return mReceivers.queryIntentForPackage(intent, resolvedType, flags, receivers, userId);
@@ -495,14 +495,14 @@
}
@Nullable
- List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags, int userId) {
+ List<ResolveInfo> queryServices(Intent intent, String resolvedType, long flags, int userId) {
synchronized (mLock) {
return mServices.queryIntent(intent, resolvedType, flags, userId);
}
}
@Nullable
- List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> queryServices(Intent intent, String resolvedType, long flags,
List<ParsedService> services, int userId) {
synchronized (mLock) {
return mServices.queryIntentForPackage(intent, resolvedType, flags, services, userId);
@@ -1380,7 +1380,7 @@
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
- List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
int userId) {
if (!sUserManager.exists(userId)) {
return null;
@@ -1392,7 +1392,7 @@
}
List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
- int flags, List<ParsedActivity> packageActivities, int userId) {
+ long flags, List<ParsedActivity> packageActivities, int userId) {
if (!sUserManager.exists(userId)) {
return null;
}
@@ -1669,7 +1669,7 @@
// ActivityIntentResolver.
protected final ArrayMap<ComponentName, ParsedActivity> mActivities =
new ArrayMap<>();
- private int mFlags;
+ private long mFlags;
}
// Both receivers and activities share a class, but point to different get methods
@@ -1711,7 +1711,7 @@
}
@Nullable
- List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
int userId) {
if (!sUserManager.exists(userId)) {
return null;
@@ -1724,7 +1724,7 @@
@Nullable
List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
- int flags, List<ParsedProvider> packageProviders, int userId) {
+ long flags, List<ParsedProvider> packageProviders, int userId) {
if (!sUserManager.exists(userId)) {
return null;
}
@@ -1946,7 +1946,7 @@
}
private final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
- private int mFlags;
+ private long mFlags;
}
private static final class ServiceIntentResolver
@@ -1969,7 +1969,7 @@
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
- List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
int userId) {
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
@@ -1979,7 +1979,7 @@
}
List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
- int flags, List<ParsedService> packageServices, int userId) {
+ long flags, List<ParsedService> packageServices, int userId) {
if (!sUserManager.exists(userId)) return null;
if (packageServices == null) {
return Collections.emptyList();
@@ -2190,7 +2190,7 @@
// Keys are String (activity class name), values are Activity.
private final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
- private int mFlags;
+ private long mFlags;
}
static final class InstantAppIntentResolver
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index a9df4ba..3e849f8 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -133,35 +133,36 @@
}
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
- int flags, @PackageManagerInternal.PrivateResolveFlags int privateResolveFlags,
+ @PackageManager.ResolveInfoFlags long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
- int flags, int userId);
+ long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType,
- int flags, int userId, int callingUid, boolean includeInstantApps);
+ long flags, int userId, int callingUid, boolean includeInstantApps);
@Computer.LiveImplementation(override = Computer.LiveImplementation.MANDATORY)
@NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
- String resolvedType, int flags, int filterCallingUid, int userId,
+ String resolvedType, long flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
String instantAppPkgName);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- ActivityInfo getActivityInfo(ComponentName component, int flags, int userId);
+ ActivityInfo getActivityInfo(ComponentName component, long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
+ ActivityInfo getActivityInfoInternal(ComponentName component, long flags,
int filterCallingUid, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.MANDATORY)
AndroidPackage getPackage(String packageName);
@Computer.LiveImplementation(override = Computer.LiveImplementation.MANDATORY)
AndroidPackage getPackage(int uid);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- ApplicationInfo generateApplicationInfoFromSettings(String packageName, int flags,
+ ApplicationInfo generateApplicationInfoFromSettings(String packageName, long flags,
int filterCallingUid, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- ApplicationInfo getApplicationInfo(String packageName, int flags, int userId);
+ ApplicationInfo getApplicationInfo(String packageName, long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
+ ApplicationInfo getApplicationInfoInternal(String packageName, long flags,
int filterCallingUid, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
ComponentName getDefaultHomeActivity(int userId);
@@ -169,7 +170,7 @@
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType,
- int flags, int sourceUserId, int parentUserId);
+ long flags, int sourceUserId, int parentUserId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
Intent getHomeIntent();
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@@ -180,11 +181,11 @@
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- PackageInfo generatePackageInfo(PackageStateInternal ps, int flags, int userId);
+ PackageInfo generatePackageInfo(PackageStateInternal ps, long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- PackageInfo getPackageInfo(String packageName, int flags, int userId);
+ PackageInfo getPackageInfo(String packageName, long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags,
+ PackageInfo getPackageInfoInternal(String packageName, long versionCode, long flags,
int filterCallingUid, int userId);
/**
@@ -202,12 +203,12 @@
@Computer.LiveImplementation(override = Computer.LiveImplementation.MANDATORY)
@Nullable PackageState getPackageStateCopied(@NonNull String packageName);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId);
+ ParceledListSlice<PackageInfo> getInstalledPackages(long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- ServiceInfo getServiceInfo(ComponentName component, int flags, int userId);
+ ServiceInfo getServiceInfo(ComponentName component, long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
SharedLibraryInfo getSharedLibraryInfo(String name, long version);
@Computer.LiveImplementation(override = Computer.LiveImplementation.MANDATORY)
@@ -224,7 +225,7 @@
boolean canViewInstantApps(int callingUid, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid, int userId,
- int flags);
+ long flags);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
boolean isCallerSameApp(String packageName, int uid);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@@ -234,7 +235,7 @@
@PackageManager.ComponentType int type);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
- String resolvedType, int flags);
+ String resolvedType, long flags);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
boolean isInstantApp(String packageName, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@@ -254,18 +255,18 @@
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
int checkUidPermission(String permName, int uid);
@Computer.LiveImplementation(override = Computer.LiveImplementation.MANDATORY)
- int getPackageUidInternal(String packageName, int flags, int userId, int callingUid);
+ int getPackageUidInternal(String packageName, long flags, int userId, int callingUid);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- int updateFlagsForApplication(int flags, int userId);
+ long updateFlagsForApplication(long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- int updateFlagsForComponent(int flags, int userId);
+ long updateFlagsForComponent(long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- int updateFlagsForPackage(int flags, int userId);
+ long updateFlagsForPackage(long flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+ long updateFlagsForResolve(long flags, int userId, int callingUid, boolean wantInstantApps,
boolean isImplicitImageCaptureIntentAndNotSetByDpc);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+ long updateFlagsForResolve(long flags, int userId, int callingUid, boolean wantInstantApps,
boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
@@ -291,10 +292,10 @@
void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityInternal(
- Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean always,
+ Intent intent, String resolvedType, long flags, List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, int flags,
+ ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, long flags,
List<ResolveInfo> query, boolean debug, int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@@ -337,7 +338,8 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
- int[] getPackageGids(@NonNull String packageName, int flags, @UserIdInt int userId);
+ int[] getPackageGids(@NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
int getTargetSdkVersion(@NonNull String packageName);
@@ -348,13 +350,13 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
- ActivityInfo getReceiverInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId);
+ ActivityInfo getReceiverInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
- int flags, @UserIdInt int userId);
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
boolean canRequestPackageInstalls(@NonNull String packageName, int callingUid,
@@ -367,17 +369,18 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- int flags, int callingUid, @UserIdInt int userId);
+ @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, int flags, @UserIdInt int userId);
+ @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
- ProviderInfo getProviderInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId);
+ ProviderInfo getProviderInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
@@ -436,17 +439,17 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(@NonNull String[] permissions,
- int flags, @UserIdInt int userId);
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
- List<ApplicationInfo> getInstalledApplications(int flags, @UserIdInt int userId,
- int callingUid);
+ List<ApplicationInfo> getInstalledApplications(@PackageManager.ApplicationInfoFlags long flags,
+ @UserIdInt int userId, int callingUid);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
- ProviderInfo resolveContentProvider(@NonNull String name, int flags,
- @UserIdInt int userId, int callingUid);
+ ProviderInfo resolveContentProvider(@NonNull String name,
+ @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
@@ -460,7 +463,7 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull
ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName, int uid,
- int flags, @Nullable String metaDataKey);
+ @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
@@ -557,7 +560,8 @@
boolean canQueryPackage(int callingUid, @Nullable String targetPackageName);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
- int getPackageUid(@NonNull String packageName, int flags, @UserIdInt int userId);
+ int getPackageUid(@NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId);
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
boolean canAccessComponent(int callingUid, @NonNull ComponentName component,
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 887dfff..b605c93 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -125,7 +125,6 @@
import android.util.TypedXmlSerializer;
import android.util.Xml;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -165,7 +164,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -215,7 +213,7 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean isEnabledAndMatch(AndroidPackage pkg, ParsedMainComponent component,
- int flags, int userId) {
+ long flags, int userId) {
PackageStateInternal pkgState = getPackage(component.getPackageName());
if (pkgState == null) {
return false;
@@ -431,8 +429,8 @@
}
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, int flags,
- @PackageManagerInternal.PrivateResolveFlags int privateResolveFlags,
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart,
boolean allowDynamicSplits) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
@@ -543,15 +541,15 @@
}
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
return queryIntentActivitiesInternal(
intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
- String resolvedType, int flags, int userId, int callingUid,
- boolean includeInstantApps) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ int callingUid, boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
enforceCrossUserOrProfilePermission(callingUid,
userId,
@@ -627,8 +625,8 @@
}
protected @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
- String resolvedType, int flags, int userId, int callingUid,
- String instantAppPkgName) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ int callingUid, String instantAppPkgName) {
// reader
String pkgName = intent.getPackage();
if (pkgName == null) {
@@ -655,9 +653,9 @@
}
public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
- Intent intent, String resolvedType, int flags, int filterCallingUid, int userId,
- boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
- String instantAppPkgName) {
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
+ String pkgName, String instantAppPkgName) {
// reader
boolean sortResult = false;
boolean addInstant = false;
@@ -787,7 +785,8 @@
return null;
}
- public final ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
+ public final ActivityInfo getActivityInfo(ComponentName component,
+ @PackageManager.ResolveInfoFlags long flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
@@ -797,8 +796,8 @@
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
- public final ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
- int filterCallingUid, int userId) {
+ public final ActivityInfo getActivityInfoInternal(ComponentName component,
+ @PackageManager.ResolveInfoFlags long flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
@@ -811,8 +810,8 @@
return getActivityInfoInternalBody(component, flags, filterCallingUid, userId);
}
- protected ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
- int filterCallingUid, int userId) {
+ protected ActivityInfo getActivityInfoInternalBody(ComponentName component,
+ @PackageManager.ResolveInfoFlags long flags, int filterCallingUid, int userId) {
ParsedActivity a = mComponentResolver.getActivity(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
@@ -852,7 +851,7 @@
}
public final ApplicationInfo generateApplicationInfoFromSettings(String packageName,
- int flags, int filterCallingUid, int userId) {
+ long flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps != null) {
@@ -879,7 +878,8 @@
return null;
}
- public final ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
+ public final ApplicationInfo getApplicationInfo(String packageName,
+ @PackageManager.ApplicationInfoFlags long flags, int userId) {
return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
}
@@ -889,7 +889,8 @@
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
- public final ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
+ public final ApplicationInfo getApplicationInfoInternal(String packageName,
+ @PackageManager.ApplicationInfoFlags long flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId);
@@ -903,7 +904,8 @@
return getApplicationInfoInternalBody(packageName, flags, filterCallingUid, userId);
}
- protected ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
+ protected ApplicationInfo getApplicationInfoInternalBody(String packageName,
+ @PackageManager.ApplicationInfoFlags long flags,
int filterCallingUid, int userId) {
// writer
// Normalize package name to handle renamed packages and static libs
@@ -960,7 +962,7 @@
}
protected ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
- Intent intent, int matchFlags, List<ResolveInfo> candidates,
+ Intent intent, long matchFlags, List<ResolveInfo> candidates,
CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
final ArrayList<ResolveInfo> result = new ArrayList<>();
final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
@@ -1159,7 +1161,8 @@
}
public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
- String resolvedType, int flags, int sourceUserId, int parentUserId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+ int parentUserId) {
if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
sourceUserId)) {
return null;
@@ -1380,7 +1383,7 @@
}
private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
- int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
+ long matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
int userId) {
final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
@@ -1423,9 +1426,8 @@
}
private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result,
- Intent intent,
- String resolvedType, int flags, int userId, boolean resolveForStart,
- boolean isRequesterInstantApp) {
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ int userId, boolean resolveForStart, boolean isRequesterInstantApp) {
// first, check to see if we've got an instant app already installed
final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
ResolveInfo localInstantApp = null;
@@ -1529,7 +1531,8 @@
return result;
}
- public final PackageInfo generatePackageInfo(PackageStateInternal ps, int flags, int userId) {
+ public final PackageInfo generatePackageInfo(PackageStateInternal ps,
+ @PackageManager.PackageInfoFlags long flags, int userId) {
if (!mUserManager.exists(userId)) return null;
if (ps == null) {
return null;
@@ -1603,7 +1606,8 @@
}
}
- public final PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+ public final PackageInfo getPackageInfo(String packageName,
+ @PackageManager.PackageInfoFlags long flags, int userId) {
return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, Binder.getCallingUid(), userId);
}
@@ -1615,7 +1619,7 @@
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
public final PackageInfo getPackageInfoInternal(String packageName, long versionCode,
- int flags, int filterCallingUid, int userId) {
+ long flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId,
@@ -1626,7 +1630,7 @@
}
protected PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
- int flags, int filterCallingUid, int userId) {
+ long flags, int filterCallingUid, int userId) {
// reader
// Normalize package name to handle renamed packages and static libs
packageName = resolveInternalPackageNameLPr(packageName, versionCode);
@@ -1711,7 +1715,7 @@
return pkgSetting == null ? null : PackageStateImpl.copy(pkgSetting);
}
- public final ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
+ public final ParceledListSlice<PackageInfo> getInstalledPackages(long flags, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return ParceledListSlice.emptyList();
@@ -1725,7 +1729,7 @@
return getInstalledPackagesBody(flags, userId, callingUid);
}
- protected ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
+ protected ParceledListSlice<PackageInfo> getInstalledPackagesBody(long flags, int userId,
int callingUid) {
// writer
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
@@ -1804,7 +1808,8 @@
@Nullable
private CrossProfileDomainInfo createForwardingResolveInfo(
@NonNull CrossProfileIntentFilter filter, @NonNull Intent intent,
- @Nullable String resolvedType, int flags, int sourceUserId) {
+ @Nullable String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ int sourceUserId) {
int targetUserId = filter.getTargetUserId();
if (!isUserEnabled(targetUserId)) {
return null;
@@ -1891,7 +1896,7 @@
@Nullable
private CrossProfileDomainInfo queryCrossProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
- int flags, int sourceUserId, boolean matchInCurrentProfile) {
+ long flags, int sourceUserId, boolean matchInCurrentProfile) {
if (matchingFilters == null) {
return null;
}
@@ -1945,7 +1950,7 @@
private ResolveInfo querySkipCurrentProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
- int flags, int sourceUserId) {
+ long flags, int sourceUserId) {
if (matchingFilters != null) {
int size = matchingFilters.size();
for (int i = 0; i < size; i++) {
@@ -1964,7 +1969,8 @@
return null;
}
- public final ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
+ public final ServiceInfo getServiceInfo(ComponentName component,
+ @PackageManager.ResolveInfoFlags long flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
@@ -1974,8 +1980,8 @@
return getServiceInfoBody(component, flags, userId, callingUid);
}
- protected ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
- int callingUid) {
+ protected ServiceInfo getServiceInfoBody(ComponentName component,
+ @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
ParsedService s = mComponentResolver.getService(component);
if (DEBUG_PACKAGE_INFO) {
Log.v(
@@ -2226,11 +2232,12 @@
return false;
}
- public final boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, int flags) {
- // Callers can access only the libs they depend on, otherwise they need to explicitly
- // ask for the shared libraries given the caller is allowed to access all static libs.
- if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
+ private boolean filterStaticSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
+ int userId, @PackageManager.ComponentInfoFlags long flags) {
+ // Callers can access only the static shared libs they depend on, otherwise they need to
+ // explicitly ask for the static shared libraries given the caller is allowed to access
+ // all static libs.
+ if ((flags & PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES) != 0) {
// System/shell/root get to see all static libs
final int appId = UserHandle.getAppId(uid);
if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
@@ -2281,6 +2288,69 @@
return true;
}
+ private boolean filterSdkLibPackage(@Nullable PackageStateInternal ps, int uid,
+ int userId, @PackageManager.ComponentInfoFlags long flags) {
+ // Callers can access only the SDK libs they depend on, otherwise they need to
+ // explicitly ask for the SDKs given the caller is allowed to access
+ // all shared libs.
+ if ((flags & PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES) != 0) {
+ // System/shell/root get to see all SDK libs.
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
+ || appId == Process.ROOT_UID) {
+ return false;
+ }
+ // Installer gets to see all SDK libs.
+ if (PackageManager.PERMISSION_GRANTED
+ == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
+ return false;
+ }
+ }
+
+ // No package means no static lib as it is always on internal storage
+ if (ps == null || ps.getPkg() == null || !ps.getPkg().isSdkLibrary()) {
+ return false;
+ }
+
+ final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(
+ ps.getPkg().getSdkLibName(), ps.getPkg().getSdkLibVersionMajor());
+ if (libraryInfo == null) {
+ return false;
+ }
+
+ final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+ final String[] uidPackageNames = getPackagesForUid(resolvedUid);
+ if (uidPackageNames == null) {
+ return true;
+ }
+
+ for (String uidPackageName : uidPackageNames) {
+ if (ps.getPackageName().equals(uidPackageName)) {
+ return false;
+ }
+ PackageStateInternal uidPs = mSettings.getPackage(uidPackageName);
+ if (uidPs != null) {
+ final int index = ArrayUtils.indexOf(uidPs.getUsesSdkLibraries(),
+ libraryInfo.getName());
+ if (index < 0) {
+ continue;
+ }
+ if (uidPs.getPkg().getUsesSdkLibrariesVersionsMajor()[index]
+ == libraryInfo.getLongVersion()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public final boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
+ int userId, @PackageManager.ComponentInfoFlags long flags) {
+ return filterStaticSharedLibPackage(ps, uid, userId, flags) && filterSdkLibPackage(ps, uid,
+ userId, flags);
+ }
+
private boolean hasCrossUserPermission(
int callingUid, int callingUserId, int userId, boolean requireFullPermission,
boolean requirePermissionWhenSameUser) {
@@ -2375,7 +2445,7 @@
* activity was not set by the DPC.
*/
public final boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
- int userId, String resolvedType, int flags) {
+ int userId, String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
intent, userId, resolvedType, flags);
}
@@ -2416,7 +2486,7 @@
private boolean isInstantAppResolutionAllowed(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
- boolean skipPackageCheck, int flags) {
+ boolean skipPackageCheck, @PackageManager.ResolveInfoFlags long flags) {
if (mInstantAppResolverConnection == null) {
return false;
}
@@ -2456,7 +2526,7 @@
// Or if there's already an ephemeral app installed that handles the action
protected boolean isInstantAppResolutionAllowedBody(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
- boolean skipPackageCheck, int flags) {
+ boolean skipPackageCheck, @PackageManager.ResolveInfoFlags long flags) {
final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
for (int n = 0; n < count; n++) {
final ResolveInfo info = resolvedActivities.get(n);
@@ -2488,7 +2558,7 @@
}
private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
- String resolvedType, int flags) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
PersistentPreferredIntentResolver ppir =
mSettings.getPersistentPreferredActivities(userId);
//TODO(b/158003772): Remove double query
@@ -2645,8 +2715,8 @@
return mPermissionManager.checkUidPermission(uid, permName);
}
- public int getPackageUidInternal(String packageName, int flags, int userId,
- int callingUid) {
+ public int getPackageUidInternal(String packageName,
+ @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
// reader
final AndroidPackage p = mPackages.get(packageName);
if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
@@ -2670,7 +2740,7 @@
/**
* Update given flags based on encryption status of current user.
*/
- private int updateFlags(int flags, int userId) {
+ private long updateFlags(long flags, int userId) {
if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {
// Caller expressed an explicit opinion about what encryption
@@ -2691,21 +2761,21 @@
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
- public final int updateFlagsForApplication(int flags, int userId) {
+ public final long updateFlagsForApplication(long flags, int userId) {
return updateFlagsForPackage(flags, userId);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
- public final int updateFlagsForComponent(int flags, int userId) {
+ public final long updateFlagsForComponent(long flags, int userId) {
return updateFlags(flags, userId);
}
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
- public final int updateFlagsForPackage(int flags, int userId) {
+ public final long updateFlagsForPackage(long flags, int userId) {
final boolean isCallerSystemUser = UserHandle.getCallingUserId()
== UserHandle.USER_SYSTEM;
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
@@ -2741,14 +2811,14 @@
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- public final int updateFlagsForResolve(int flags, int userId, int callingUid,
+ public final long updateFlagsForResolve(long flags, int userId, int callingUid,
boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
return updateFlagsForResolve(flags, userId, callingUid,
wantInstantApps, false /*onlyExposedExplicitly*/,
isImplicitImageCaptureIntentAndNotSetByDpc);
}
- public final int updateFlagsForResolve(int flags, int userId, int callingUid,
+ public final long updateFlagsForResolve(long flags, int userId, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly,
boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
// Safe mode means we shouldn't match any third-party components
@@ -3169,7 +3239,7 @@
// The body of findPreferredActivity.
protected PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityBody(
- Intent intent, String resolvedType, int flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered,
int callingUid, boolean isDeviceProvisioned) {
@@ -3378,7 +3448,7 @@
}
public final PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityInternal(
- Intent intent, String resolvedType, int flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
@@ -3395,8 +3465,8 @@
}
public final ResolveInfo findPersistentPreferredActivityLP(Intent intent,
- String resolvedType,
- int flags, List<ResolveInfo> query, boolean debug, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ List<ResolveInfo> query, boolean debug, int userId) {
final int n = query.size();
PersistentPreferredIntentResolver ppir =
mSettings.getPersistentPreferredActivities(userId);
@@ -3592,7 +3662,8 @@
}
@Override
- public int[] getPackageGids(@NonNull String packageName, int flags, @UserIdInt int userId) {
+ public int[] getPackageGids(@NonNull String packageName,
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
@@ -3668,8 +3739,8 @@
@Nullable
@Override
- public ActivityInfo getReceiverInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId) {
+ public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
@@ -3702,7 +3773,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
- int flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return null;
Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
final int callingUid = Binder.getCallingUid();
@@ -3712,7 +3783,7 @@
flags = updateFlagsForPackage(flags, userId);
- final boolean canSeeStaticLibraries =
+ final boolean canSeeStaticAndSdkLibraries =
mContext.checkCallingOrSelfPermission(INSTALL_PACKAGES)
== PERMISSION_GRANTED
|| mContext.checkCallingOrSelfPermission(DELETE_PACKAGES)
@@ -3737,7 +3808,7 @@
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libInfo = versionedLib.valueAt(j);
- if (!canSeeStaticLibraries && libInfo.isStatic()) {
+ if (!canSeeStaticAndSdkLibraries && (libInfo.isStatic() || libInfo.isSdk())) {
break;
}
final long identity = Binder.clearCallingIdentity();
@@ -3746,7 +3817,7 @@
PackageInfo packageInfo = getPackageInfoInternal(
declaringPackage.getPackageName(),
declaringPackage.getLongVersionCode(),
- flags | PackageManager.MATCH_STATIC_SHARED_LIBRARIES,
+ flags | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
Binder.getCallingUid(), userId);
if (packageInfo == null) {
continue;
@@ -3831,7 +3902,7 @@
@Override
public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- int flags, int callingUid, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
List<VersionedPackage> versionedPackages = null;
final ArrayMap<String, ? extends PackageStateInternal> packageStates = getPackageStates();
final int packageCount = packageStates.size();
@@ -3846,12 +3917,17 @@
}
final String libName = libInfo.getName();
- if (libInfo.isStatic()) {
- final int libIdx = ArrayUtils.indexOf(ps.getUsesStaticLibraries(), libName);
+ if (libInfo.isStatic() || libInfo.isSdk()) {
+ final String[] libs =
+ libInfo.isStatic() ? ps.getUsesStaticLibraries() : ps.getUsesSdkLibraries();
+ final long[] libsVersions = libInfo.isStatic() ? ps.getUsesStaticLibrariesVersions()
+ : ps.getUsesSdkLibrariesVersionsMajor();
+
+ final int libIdx = ArrayUtils.indexOf(libs, libName);
if (libIdx < 0) {
continue;
}
- if (ps.getUsesStaticLibrariesVersions()[libIdx] != libInfo.getLongVersion()) {
+ if (libsVersions[libIdx] != libInfo.getLongVersion()) {
continue;
}
if (shouldFilterApplication(ps, callingUid, userId)) {
@@ -3888,7 +3964,8 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, int flags, @UserIdInt int userId) {
+ @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES,
"getDeclaredSharedLibraries");
int callingUid = Binder.getCallingUid();
@@ -3931,7 +4008,7 @@
PackageInfo packageInfo = getPackageInfoInternal(
declaringPackage.getPackageName(),
declaringPackage.getLongVersionCode(),
- flags | PackageManager.MATCH_STATIC_SHARED_LIBRARIES,
+ flags | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
Binder.getCallingUid(), userId);
if (packageInfo == null) {
continue;
@@ -3963,8 +4040,8 @@
@Nullable
@Override
- public ProviderInfo getProviderInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId) {
+ public ProviderInfo getProviderInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
@@ -4026,7 +4103,7 @@
getPackageStateInternal(libraryInfo.getPackageName());
if (ps != null && !filterSharedLibPackage(ps, Binder.getCallingUid(),
UserHandle.getUserId(Binder.getCallingUid()),
- PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
+ PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES)) {
if (libs == null) {
libs = new ArraySet<>();
}
@@ -4438,7 +4515,8 @@
@NonNull
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, int flags, @UserIdInt int userId) {
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId);
enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
@@ -4458,7 +4536,8 @@
}
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageStateInternal ps,
- String[] permissions, boolean[] tmp, int flags, int userId) {
+ String[] permissions, boolean[] tmp, @PackageManager.PackageInfoFlags long flags,
+ int userId) {
int numMatch = 0;
for (int i=0; i<permissions.length; i++) {
final String permission = permissions[i];
@@ -4478,7 +4557,7 @@
// The above might return null in cases of uninstalled apps or install-state
// skew across users/profiles.
if (pi != null) {
- if ((flags&PackageManager.GET_PERMISSIONS) == 0) {
+ if ((flags & PackageManager.GET_PERMISSIONS) == 0) {
if (numMatch == permissions.length) {
pi.requestedPermissions = permissions;
} else {
@@ -4498,7 +4577,8 @@
@NonNull
@Override
- public List<ApplicationInfo> getInstalledApplications(int flags, @UserIdInt int userId,
+ public List<ApplicationInfo> getInstalledApplications(
+ @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
int callingUid) {
if (getInstantAppPackageName(callingUid) != null) {
return Collections.emptyList();
@@ -4521,7 +4601,7 @@
list = new ArrayList<>(packageStates.size());
for (PackageStateInternal ps : packageStates.values()) {
ApplicationInfo ai;
- int effectiveFlags = flags;
+ long effectiveFlags = flags;
if (ps.isSystem()) {
effectiveFlags |= PackageManager.MATCH_ANY_USER;
}
@@ -4574,8 +4654,8 @@
@Nullable
@Override
- public ProviderInfo resolveContentProvider(@NonNull String name, int flags,
- @UserIdInt int userId, int callingUid) {
+ public ProviderInfo resolveContentProvider(@NonNull String name,
+ @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
@@ -4664,7 +4744,7 @@
@NonNull
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, int flags, @Nullable String metaDataKey) {
+ int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
final int callingUid = Binder.getCallingUid();
final int userId = processName != null ? UserHandle.getUserId(uid)
: UserHandle.getCallingUserId();
@@ -5209,7 +5289,8 @@
}
@Override
- public int getPackageUid(@NonNull String packageName, int flags, @UserIdInt int userId) {
+ public int getPackageUid(@NonNull String packageName,
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
@@ -5312,7 +5393,7 @@
if (parent == null) {
return false;
}
- int flags = updateFlagsForResolve(0, parent.id, callingUid,
+ long flags = updateFlagsForResolve(0, parent.id, callingUid,
false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, parent.id,
resolvedType, 0));
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index 801aaef..f180d19 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -29,6 +29,7 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.KeySet;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
@@ -89,7 +90,7 @@
}
}
public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
- Intent intent, String resolvedType, int flags, int filterCallingUid, int userId,
+ Intent intent, String resolvedType, long flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
String instantAppPkgName) {
synchronized (mLock) {
@@ -312,7 +313,8 @@
}
@Override
- public int[] getPackageGids(@NonNull String packageName, int flags, @UserIdInt int userId) {
+ public int[] getPackageGids(@NonNull String packageName,
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getPackageGids(packageName, flags, userId);
}
@@ -336,8 +338,8 @@
@Nullable
@Override
- public ActivityInfo getReceiverInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId) {
+ public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getReceiverInfo(component, flags, userId);
}
@@ -346,7 +348,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
- int flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getSharedLibraries(packageName, flags, userId);
}
@@ -363,7 +365,7 @@
@Override
public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- int flags, int callingUid, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
synchronized (mLock) {
return super.getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
}
@@ -372,7 +374,8 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, int flags, @UserIdInt int userId) {
+ @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId) {
synchronized (mLock) {
return super.getDeclaredSharedLibraries(packageName, flags, userId);
}
@@ -380,8 +383,8 @@
@Nullable
@Override
- public ProviderInfo getProviderInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId) {
+ public ProviderInfo getProviderInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getProviderInfo(component, flags, userId);
}
@@ -495,7 +498,8 @@
@NonNull
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, int flags, @UserIdInt int userId) {
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId) {
synchronized (mLock) {
return super.getPackagesHoldingPermissions(permissions, flags, userId);
}
@@ -503,7 +507,8 @@
@NonNull
@Override
- public List<ApplicationInfo> getInstalledApplications(int flags, @UserIdInt int userId,
+ public List<ApplicationInfo> getInstalledApplications(
+ @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
int callingUid) {
synchronized (mLock) {
return super.getInstalledApplications(flags, userId, callingUid);
@@ -512,8 +517,8 @@
@Nullable
@Override
- public ProviderInfo resolveContentProvider(@NonNull String name, int flags,
- @UserIdInt int userId, int callingUid) {
+ public ProviderInfo resolveContentProvider(@NonNull String name,
+ @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
synchronized (mLock) {
return super.resolveContentProvider(name, flags, userId, callingUid);
}
@@ -539,7 +544,7 @@
@NonNull
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, int flags, @Nullable String metaDataKey) {
+ int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
synchronized (mLock) {
return super.queryContentProviders(processName, uid, flags, metaDataKey);
}
@@ -711,7 +716,8 @@
}
@Override
- public int getPackageUid(@NonNull String packageName, int flags, @UserIdInt int userId) {
+ public int getPackageUid(@NonNull String packageName,
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
synchronized (mLock) {
return super.getPackageUid(packageName, flags, userId);
}
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index ca17d66..a3cd092 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -97,8 +97,8 @@
}
public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, int flags,
- @PackageManagerInternal.PrivateResolveFlags int privateResolveFlags,
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart,
boolean allowDynamicSplits) {
ThreadComputer current = snapshot();
@@ -111,7 +111,7 @@
}
}
public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.queryIntentActivitiesInternal(intent, resolvedType, flags,
@@ -121,8 +121,8 @@
}
}
public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
- String resolvedType, int flags, int userId, int callingUid,
- boolean includeInstantApps) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ int callingUid, boolean includeInstantApps) {
ThreadComputer current = snapshot();
try {
return current.mComputer.queryIntentServicesInternal(intent, resolvedType, flags,
@@ -132,10 +132,9 @@
}
}
public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
- Intent intent,
- String resolvedType, int flags, int filterCallingUid, int userId,
- boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
- String instantAppPkgName) {
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
+ String pkgName, String instantAppPkgName) {
ThreadComputer current = live();
try {
return current.mComputer.queryIntentActivitiesInternalBody(intent, resolvedType,
@@ -145,7 +144,8 @@
current.release();
}
}
- public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
+ public ActivityInfo getActivityInfo(ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.getActivityInfo(component, flags, userId);
@@ -153,7 +153,8 @@
current.release();
}
}
- public ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
+ public ActivityInfo getActivityInfoInternal(ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags,
int filterCallingUid, int userId) {
ThreadComputer current = live();
try {
@@ -180,7 +181,7 @@
}
}
public ApplicationInfo generateApplicationInfoFromSettings(String packageName,
- int flags, int filterCallingUid, int userId) {
+ long flags, int filterCallingUid, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.generateApplicationInfoFromSettings(packageName, flags,
@@ -189,7 +190,8 @@
current.release();
}
}
- public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
+ public ApplicationInfo getApplicationInfo(String packageName,
+ @PackageManager.ApplicationInfoFlags long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.getApplicationInfo(packageName, flags, userId);
@@ -197,8 +199,8 @@
current.release();
}
}
- public ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
- int filterCallingUid, int userId) {
+ public ApplicationInfo getApplicationInfoInternal(String packageName,
+ @PackageManager.ApplicationInfoFlags long flags, int filterCallingUid, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.getApplicationInfoInternal(packageName, flags,
@@ -225,7 +227,8 @@
}
}
public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
- String resolvedType, int flags, int sourceUserId, int parentUserId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+ int parentUserId) {
ThreadComputer current = live();
try {
return current.mComputer.getCrossProfileDomainPreferredLpr(intent, resolvedType,
@@ -264,7 +267,8 @@
current.release();
}
}
- public PackageInfo generatePackageInfo(PackageStateInternal ps, int flags, int userId) {
+ public PackageInfo generatePackageInfo(PackageStateInternal ps,
+ @PackageManager.PackageInfoFlags long flags, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.generatePackageInfo(ps, flags, userId);
@@ -272,7 +276,8 @@
current.release();
}
}
- public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+ public PackageInfo getPackageInfo(String packageName,
+ @PackageManager.PackageInfoFlags long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.getPackageInfo(packageName, flags, userId);
@@ -281,7 +286,7 @@
}
}
public PackageInfo getPackageInfoInternal(String packageName, long versionCode,
- int flags, int filterCallingUid, int userId) {
+ long flags, int filterCallingUid, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.getPackageInfoInternal(packageName, versionCode, flags,
@@ -317,7 +322,7 @@
}
}
- public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
+ public ParceledListSlice<PackageInfo> getInstalledPackages(long flags, int userId) {
ThreadComputer current = snapshot();
try {
return current.mComputer.getInstalledPackages(flags, userId);
@@ -335,7 +340,8 @@
current.release();
}
}
- public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
+ public ServiceInfo getServiceInfo(ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.getServiceInfo(component, flags, userId);
@@ -440,7 +446,7 @@
}
}
public boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, int flags) {
+ int userId, @PackageManager.ComponentInfoFlags long flags) {
ThreadComputer current = live();
try {
return current.mComputer.filterSharedLibPackage(ps, uid, userId, flags);
@@ -474,7 +480,7 @@
}
}
public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
- int userId, String resolvedType, int flags) {
+ int userId, String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
ThreadComputer current = live();
try {
return current.mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent,
@@ -546,8 +552,8 @@
current.release();
}
}
- public int getPackageUidInternal(String packageName, int flags, int userId,
- int callingUid) {
+ public int getPackageUidInternal(String packageName,
+ @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
ThreadComputer current = live();
try {
return current.mComputer.getPackageUidInternal(packageName, flags, userId,
@@ -556,7 +562,7 @@
current.release();
}
}
- public int updateFlagsForApplication(int flags, int userId) {
+ public long updateFlagsForApplication(long flags, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.updateFlagsForApplication(flags, userId);
@@ -564,7 +570,7 @@
current.release();
}
}
- public int updateFlagsForComponent(int flags, int userId) {
+ public long updateFlagsForComponent(long flags, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.updateFlagsForComponent(flags, userId);
@@ -572,7 +578,7 @@
current.release();
}
}
- public int updateFlagsForPackage(int flags, int userId) {
+ public long updateFlagsForPackage(long flags, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.updateFlagsForPackage(flags, userId);
@@ -580,7 +586,7 @@
current.release();
}
}
- public int updateFlagsForResolve(int flags, int userId, int callingUid,
+ public long updateFlagsForResolve(long flags, int userId, int callingUid,
boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
ThreadComputer current = snapshot();
try {
@@ -590,7 +596,7 @@
current.release();
}
}
- public int updateFlagsForResolve(int flags, int userId, int callingUid,
+ public long updateFlagsForResolve(long flags, int userId, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly,
boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
ThreadComputer current = live();
@@ -642,8 +648,9 @@
}
}
public PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityInternal(
- Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean always,
- boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
+ int userId, boolean queryMayBeFiltered) {
ThreadComputer current = live();
try {
return current.mComputer.findPreferredActivityInternal(intent, resolvedType, flags,
@@ -653,8 +660,8 @@
}
}
public ResolveInfo findPersistentPreferredActivityLP(Intent intent,
- String resolvedType, int flags, List<ResolveInfo> query, boolean debug,
- int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ List<ResolveInfo> query, boolean debug, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.findPersistentPreferredActivityLP(intent, resolvedType,
@@ -741,7 +748,8 @@
}
@Override
- public int[] getPackageGids(@NonNull String packageName, int flags, @UserIdInt int userId) {
+ public int[] getPackageGids(@NonNull String packageName,
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getPackageGids(packageName, flags, userId);
}
@@ -765,8 +773,8 @@
@Nullable
@Override
- public ActivityInfo getReceiverInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId) {
+ public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getReceiverInfo(component, flags, userId);
}
@@ -775,7 +783,7 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
- int flags, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getSharedLibraries(packageName, flags, userId);
}
@@ -800,7 +808,7 @@
@Override
public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
- int flags, int callingUid, @UserIdInt int userId) {
+ @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getPackagesUsingSharedLibrary(libInfo, flags, callingUid,
userId);
@@ -810,7 +818,8 @@
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, int flags, @UserIdInt int userId) {
+ @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getDeclaredSharedLibraries(packageName, flags, userId);
}
@@ -818,8 +827,8 @@
@Nullable
@Override
- public ProviderInfo getProviderInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId) {
+ public ProviderInfo getProviderInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getProviderInfo(component, flags, userId);
}
@@ -934,7 +943,8 @@
@NonNull
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, int flags, @UserIdInt int userId) {
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getPackagesHoldingPermissions(permissions, flags, userId);
}
@@ -942,7 +952,8 @@
@NonNull
@Override
- public List<ApplicationInfo> getInstalledApplications(int flags, @UserIdInt int userId,
+ public List<ApplicationInfo> getInstalledApplications(
+ @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
int callingUid) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getInstalledApplications(flags, userId, callingUid);
@@ -951,8 +962,8 @@
@Nullable
@Override
- public ProviderInfo resolveContentProvider(@NonNull String name, int flags,
- @UserIdInt int userId, int callingUid) {
+ public ProviderInfo resolveContentProvider(@NonNull String name,
+ @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.resolveContentProvider(name, flags, userId, callingUid);
}
@@ -979,7 +990,7 @@
@NonNull
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, int flags, @Nullable String metaDataKey) {
+ int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.queryContentProviders(processName, uid, flags, metaDataKey);
}
@@ -1152,7 +1163,8 @@
}
@Override
- public int getPackageUid(@NonNull String packageName, int flags, @UserIdInt int userId) {
+ public int getPackageUid(@NonNull String packageName,
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getPackageUid(packageName, flags, userId);
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 43d60cc..641f24f 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -164,9 +164,16 @@
allUsers = mUserManagerInternal.getUserIds();
- if (pkg != null && pkg.getStaticSharedLibName() != null) {
- SharedLibraryInfo libraryInfo = mPm.getSharedLibraryInfo(
- pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
+ if (pkg != null) {
+ SharedLibraryInfo libraryInfo = null;
+ if (pkg.getStaticSharedLibName() != null) {
+ libraryInfo = mPm.getSharedLibraryInfo(pkg.getStaticSharedLibName(),
+ pkg.getStaticSharedLibVersion());
+ } else if (pkg.getSdkLibName() != null) {
+ libraryInfo = mPm.getSharedLibraryInfo(pkg.getSdkLibName(),
+ pkg.getSdkLibVersionMajor());
+ }
+
if (libraryInfo != null) {
for (int currUserId : allUsers) {
if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
@@ -828,9 +835,10 @@
continue;
}
final String packageName = ps.getPkg().getPackageName();
- // Skip over if system app or static shared library
+ // Skip over if system app, static shared library or and SDK library.
if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0
- || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())) {
+ || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())
+ || !TextUtils.isEmpty(ps.getPkg().getSdkLibName())) {
continue;
}
if (DEBUG_CLEAN_APKS) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 39ed9c2..d2087ee 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -254,12 +254,11 @@
final List<SharedLibraryInfo> allowedSharedLibInfos =
SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
request.mSharedLibrarySource);
- final SharedLibraryInfo staticLib = scanResult.mStaticSharedLibraryInfo;
if (allowedSharedLibInfos != null) {
for (SharedLibraryInfo info : allowedSharedLibInfos) {
if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
incomingSharedLibraries, info)) {
- throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
+ throw new ReconcileFailure("Shared Library " + info.getName()
+ " is being installed twice in this set!");
}
}
@@ -649,12 +648,17 @@
mPm.checkPackageFrozen(pkgName);
}
- // Also need to kill any apps that are dependent on the library.
+ final boolean isReplace =
+ reconciledPkg.mPrepareResult != null && reconciledPkg.mPrepareResult.mReplace;
+ // Also need to kill any apps that are dependent on the library, except the case of
+ // installation of new version static shared library.
if (clientLibPkgs != null) {
- for (int i = 0; i < clientLibPkgs.size(); i++) {
- AndroidPackage clientPkg = clientLibPkgs.get(i);
- mPm.killApplication(clientPkg.getPackageName(),
- clientPkg.getUid(), "update lib");
+ if (pkg.getStaticSharedLibName() == null || isReplace) {
+ for (int i = 0; i < clientLibPkgs.size(); i++) {
+ AndroidPackage clientPkg = clientLibPkgs.get(i);
+ mPm.killApplication(clientPkg.getPackageName(),
+ clientPkg.getUid(), "update lib");
+ }
}
}
@@ -676,8 +680,6 @@
ksms.addScannedPackageLPw(pkg);
mPm.mComponentResolver.addAllComponents(pkg, chatty);
- final boolean isReplace =
- reconciledPkg.mPrepareResult != null && reconciledPkg.mPrepareResult.mReplace;
mPm.mAppsFilter.addPackage(pkgSetting, isReplace);
mPm.addAllPackageProperties(pkg);
@@ -1185,7 +1187,8 @@
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
- if (result.mStaticSharedLibraryInfo != null) {
+ if (result.mStaticSharedLibraryInfo != null
+ || result.mSdkSharedLibraryInfo != null) {
final PackageSetting sharedLibLatestVersionSetting =
mPm.getSharedLibLatestVersionSetting(result);
if (sharedLibLatestVersionSetting != null) {
@@ -2050,6 +2053,7 @@
}
}
+ final String packageName = pkg.getPackageName();
for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
final String filePath = entry.getKey();
final String signaturePath = entry.getValue();
@@ -2077,10 +2081,13 @@
try {
// A file may already have fs-verity, e.g. when reused during a split
// install. If the measurement succeeds, no need to attempt to set up.
- mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, filePath,
+ rootHash);
} catch (Installer.InstallerException e) {
- mPm.mInstaller.installApkVerity(filePath, fd, result.getContentSize());
- mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+ mPm.mInstaller.installApkVerity(packageName, filePath, fd,
+ result.getContentSize());
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, filePath,
+ rootHash);
}
} finally {
IoUtils.closeQuietly(fd);
@@ -2349,10 +2356,9 @@
// Set install reason for users that are having the package newly installed.
final int[] allUsersList = mPm.mUserManager.getUserIds();
if (userId == UserHandle.USER_ALL) {
- // TODO(b/152629990): It appears that the package doesn't actually get newly
- // installed in this case, so the installReason shouldn't get modified?
for (int currentUserId : allUsersList) {
- if (!previousUserIds.contains(currentUserId)) {
+ if (!previousUserIds.contains(currentUserId)
+ && ps.getInstalled(currentUserId)) {
ps.setInstallReason(installReason, currentUserId);
}
}
@@ -2689,7 +2695,7 @@
}
}
- if (dataOwnerPkg != null) {
+ if (dataOwnerPkg != null && !dataOwnerPkg.isSdkLibrary()) {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.isDebuggable())) {
try {
@@ -3126,10 +3132,12 @@
true, true, pkgList, uidArray, null);
}
} else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
+ // No need to kill consumers if it's installation of new version static shared lib.
+ final boolean dontKillApp = !update && res.mPkg.getStaticSharedLibName() != null;
for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
AndroidPackage pkg = res.mLibraryConsumers.get(i);
// send broadcast that all consumers of the static shared library have changed
- mPm.sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
+ mPm.sendPackageChangedBroadcast(pkg.getPackageName(), dontKillApp,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
pkg.getUid(), null);
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index c8bd2c0..a380344 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -497,7 +497,7 @@
*
* @throws InstallerException if {@code dexopt} fails.
*/
- public boolean dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
+ public boolean dexopt(String apkPath, int uid, String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String classLoaderContext,
@Nullable String seInfo, boolean downgrade, int targetSdkVersion,
@@ -585,11 +585,14 @@
}
}
- public void rmPackageDir(String packageDir) throws InstallerException {
+ /**
+ * Remove a directory belonging to a package.
+ */
+ public void rmPackageDir(String packageName, String packageDir) throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(packageDir);
try {
- mInstalld.rmPackageDir(packageDir);
+ mInstalld.rmPackageDir(packageName, packageDir);
} catch (Exception e) {
throw InstallerException.from(e);
}
@@ -662,35 +665,44 @@
}
}
- public void createOatDir(String oatDir, String dexInstructionSet)
+ /**
+ * Creates an oat dir for given package and instruction set.
+ */
+ public void createOatDir(String packageName, String oatDir, String dexInstructionSet)
throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- mInstalld.createOatDir(oatDir, dexInstructionSet);
+ mInstalld.createOatDir(packageName, oatDir, dexInstructionSet);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public void linkFile(String relativePath, String fromBase, String toBase)
+ /**
+ * Creates a hardlink for a path.
+ */
+ public void linkFile(String packageName, String relativePath, String fromBase, String toBase)
throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(fromBase);
BlockGuard.getVmPolicy().onPathAccess(toBase);
try {
- mInstalld.linkFile(relativePath, fromBase, toBase);
+ mInstalld.linkFile(packageName, relativePath, fromBase, toBase);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public void moveAb(String apkPath, String instructionSet, String outputPath)
+ /**
+ * Moves oat/vdex/art from "B" set defined by ro.boot.slot_suffix to the default set.
+ */
+ public void moveAb(String packageName, String apkPath, String instructionSet, String outputPath)
throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(apkPath);
BlockGuard.getVmPolicy().onPathAccess(outputPath);
try {
- mInstalld.moveAb(apkPath, instructionSet, outputPath);
+ mInstalld.moveAb(packageName, apkPath, instructionSet, outputPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
@@ -700,35 +712,41 @@
* Deletes the optimized artifacts generated by ART and returns the number
* of freed bytes.
*/
- public long deleteOdex(String apkPath, String instructionSet, String outputPath)
- throws InstallerException {
+ public long deleteOdex(String packageName, String apkPath, String instructionSet,
+ String outputPath) throws InstallerException {
if (!checkBeforeRemote()) return -1;
BlockGuard.getVmPolicy().onPathAccess(apkPath);
BlockGuard.getVmPolicy().onPathAccess(outputPath);
try {
- return mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
+ return mInstalld.deleteOdex(packageName, apkPath, instructionSet, outputPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize)
- throws InstallerException {
+ /**
+ * Enables legacy apk-verity for an apk.
+ */
+ public void installApkVerity(String packageName, String filePath, FileDescriptor verityInput,
+ int contentSize) throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(filePath);
try {
- mInstalld.installApkVerity(filePath, verityInput, contentSize);
+ mInstalld.installApkVerity(packageName, filePath, verityInput, contentSize);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public void assertFsverityRootHashMatches(String filePath, @NonNull byte[] expectedHash)
- throws InstallerException {
+ /**
+ * Checks if provided hash matches the file's fs-verity merkle tree root hash.
+ */
+ public void assertFsverityRootHashMatches(String packageName, String filePath,
+ @NonNull byte[] expectedHash) throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(filePath);
try {
- mInstalld.assertFsverityRootHashMatches(filePath, expectedHash);
+ mInstalld.assertFsverityRootHashMatches(packageName, filePath, expectedHash);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 9e6f4f7..c125fe1 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -411,6 +411,7 @@
final List<String> paths =
AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
+ final String packageName = pkg.getPackageName();
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (String path : paths) {
String oatDir = PackageDexOptimizer.getOatDir(
@@ -420,7 +421,7 @@
packagePaths++;
try {
- installer.moveAb(path, dexCodeInstructionSet, oatDir);
+ installer.moveAb(packageName, path, dexCodeInstructionSet, oatDir);
pathsSuccessful++;
} catch (InstallerException e) {
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index de1c2ad..26a5bbb 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -123,6 +123,8 @@
private static final String TAG = "PackageInstaller";
private static final boolean LOGD = false;
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE;
+
// TODO: remove outstanding sessions when installer package goes away
// TODO: notify listeners in other users when package has been installed there
// TODO: purge expired sessions periodically in addition to at reboot
@@ -411,13 +413,6 @@
removeStagingDirs(unclaimedStagingDirsOnVolume);
}
- public static boolean isStageName(String name) {
- final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
- final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
- final boolean isLegacyContainer = name.startsWith("smdl2tmp");
- return isFile || isContainer || isLegacyContainer;
- }
-
@Deprecated
public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
synchronized (mSessions) {
@@ -935,6 +930,23 @@
throw new IllegalStateException("Failed to allocate session ID");
}
+ static boolean isStageName(String name) {
+ final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
+ final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
+ final boolean isLegacyContainer = name.startsWith("smdl2tmp");
+ return isFile || isContainer || isLegacyContainer;
+ }
+
+ static int tryParseSessionId(@NonNull String tmpSessionDir)
+ throws IllegalArgumentException {
+ if (!tmpSessionDir.startsWith("vmdl") || !tmpSessionDir.endsWith(".tmp")) {
+ throw new IllegalArgumentException("Not a temporary session directory");
+ }
+ String sessionId = tmpSessionDir.substring("vmdl".length(),
+ tmpSessionDir.length() - ".tmp".length());
+ return Integer.parseInt(sessionId);
+ }
+
private File getTmpSessionDir(String volumeUuid) {
return Environment.getDataAppDirectory(volumeUuid);
}
@@ -949,7 +961,12 @@
final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
return new File(sessionStagingDir, "session_" + sessionId);
}
- return buildTmpSessionDir(sessionId, params.volumeUuid);
+ final File result = buildTmpSessionDir(sessionId, params.volumeUuid);
+ if (DEBUG && !Objects.equals(tryParseSessionId(result.getName()), sessionId)) {
+ throw new RuntimeException(
+ "session folder format is off: " + result.getName() + " (" + sessionId + ")");
+ }
+ return result;
}
static void prepareStageDir(File stageDir) throws IOException {
@@ -1311,7 +1328,7 @@
PackageInfo packageInfo = null;
try {
packageInfo = AppGlobals.getPackageManager().getPackageInfo(
- basePackageName, PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
+ basePackageName, PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES, userId);
} catch (RemoteException ignored) {
}
if (packageInfo == null || packageInfo.applicationInfo == null) {
@@ -1444,8 +1461,9 @@
private TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>> mSessionMap;
private final Comparator<PackageInstallerSession> mSessionCreationComparator =
- Comparator.comparingLong((PackageInstallerSession sess) -> sess.createdMillis)
- .thenComparingInt(sess -> sess.sessionId);
+ Comparator.comparingLong(
+ (PackageInstallerSession sess) -> sess != null ? sess.createdMillis : -1)
+ .thenComparingInt(sess -> sess != null ? sess.sessionId : -1);
ParentChildSessionMap() {
mSessionMap = new TreeMap<>(mSessionCreationComparator);
@@ -1483,10 +1501,12 @@
for (Map.Entry<PackageInstallerSession, TreeSet<PackageInstallerSession>> entry
: mSessionMap.entrySet()) {
PackageInstallerSession parentSession = entry.getKey();
- pw.print(tag + " ");
- parentSession.dump(pw);
- pw.println();
- pw.increaseIndent();
+ if (parentSession != null) {
+ pw.print(tag + " ");
+ parentSession.dump(pw);
+ pw.println();
+ pw.increaseIndent();
+ }
for (PackageInstallerSession childSession : entry.getValue()) {
pw.print(tag + " Child ");
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 803a283..55a0c96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -70,6 +70,7 @@
import android.content.pm.FileSystemControlParcel;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.IPackageInstallerSessionFileSystemConnector;
@@ -166,6 +167,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileFilter;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
@@ -1397,6 +1399,20 @@
}
@Override
+ public void requestChecksums(@NonNull String name, @Checksum.TypeMask int optional,
+ @Checksum.TypeMask int required, @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener) {
+ final File file = new File(stageDir, name);
+ final String installerPackageName = getInstallSource().initiatingPackageName;
+ try {
+ mPm.requestFileChecksums(file, installerPackageName, optional, required,
+ trustedInstallers, onChecksumsReadyListener);
+ } catch (FileNotFoundException e) {
+ throw new ParcelableException(e);
+ }
+ }
+
+ @Override
public void removeSplit(String splitName) {
if (isDataLoaderInstallation()) {
throw new IllegalStateException(
@@ -2402,6 +2418,7 @@
try {
final List<File> fromFiles = mResolvedInheritedFiles;
final File toDir = stageDir;
+ final String tempPackageName = toDir.getName();
if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
@@ -2411,7 +2428,7 @@
if (isLinkPossible(fromFiles, toDir)) {
if (!mResolvedInstructionSets.isEmpty()) {
final File oatDir = new File(toDir, "oat");
- createOatDirs(mResolvedInstructionSets, oatDir);
+ createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir);
}
// pre-create lib dirs for linking if necessary
if (!mResolvedNativeLibPaths.isEmpty()) {
@@ -2434,7 +2451,7 @@
new File(libDir, archDirPath));
}
}
- linkFiles(fromFiles, toDir, mInheritedFilesBase);
+ linkFiles(tempPackageName, fromFiles, toDir, mInheritedFilesBase);
} else {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
@@ -2884,7 +2901,7 @@
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
- | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+ | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES /*flags*/, userId);
// Partial installs must be consistent with existing install
if (params.mode == SessionParams.MODE_INHERIT_EXISTING
@@ -3529,18 +3546,19 @@
throw new IOException("File: " + pathStr + " outside base: " + baseStr);
}
- private void createOatDirs(List<String> instructionSets, File fromDir)
+ private void createOatDirs(String packageName, List<String> instructionSets, File fromDir)
throws PackageManagerException {
for (String instructionSet : instructionSets) {
try {
- mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+ mInstaller.createOatDir(packageName, fromDir.getAbsolutePath(), instructionSet);
} catch (InstallerException e) {
throw PackageManagerException.from(e);
}
}
}
- private void linkFile(String relativePath, String fromBase, String toBase) throws IOException {
+ private void linkFile(String packageName, String relativePath, String fromBase, String toBase)
+ throws IOException {
try {
// Try
final IncrementalFileStorages incrementalFileStorages = getIncrementalFileStorages();
@@ -3548,21 +3566,21 @@
fromBase, toBase)) {
return;
}
- mInstaller.linkFile(relativePath, fromBase, toBase);
+ mInstaller.linkFile(packageName, relativePath, fromBase, toBase);
} catch (InstallerException | IOException e) {
throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
+ fromBase + ", " + toBase + ")", e);
}
}
- private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
+ private void linkFiles(String packageName, List<File> fromFiles, File toDir, File fromDir)
throws IOException {
for (File fromFile : fromFiles) {
final String relativePath = getRelativePath(fromFile, fromDir);
final String fromBase = fromDir.getAbsolutePath();
final String toBase = toDir.getAbsolutePath();
- linkFile(relativePath, fromBase, toBase);
+ linkFile(packageName, relativePath, fromBase, toBase);
}
Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
@@ -4299,7 +4317,8 @@
incrementalFileStorages.cleanUpAndMarkComplete();
}
if (stageDir != null) {
- mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ final String tempPackageName = stageDir.getName();
+ mInstaller.rmPackageDir(tempPackageName, stageDir.getAbsolutePath());
}
} catch (InstallerException ignored) {
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 27288b9..fe5bce13 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -262,6 +262,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
@@ -554,6 +555,7 @@
public static final int REASON_LAST = REASON_SHARED;
static final String RANDOM_DIR_PREFIX = "~~";
+ static final char RANDOM_CODEPATH_PREFIX = '-';
final Handler mHandler;
@@ -1261,15 +1263,50 @@
}
@Override
- public void requestChecksums(@NonNull String packageName, boolean includeSplits,
- @Checksum.TypeMask int optional,
- @Checksum.TypeMask int required, @Nullable List trustedInstallers,
+ public void requestPackageChecksums(@NonNull String packageName, boolean includeSplits,
+ @Checksum.TypeMask int optional, @Checksum.TypeMask int required,
+ @Nullable List trustedInstallers,
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
mInjector.getBackgroundHandler());
}
+ /**
+ * Requests checksums for the APK file.
+ * See {@link PackageInstaller.Session#requestChecksums} for details.
+ */
+ public void requestFileChecksums(@NonNull File file,
+ @NonNull String installerPackageName, @Checksum.TypeMask int optional,
+ @Checksum.TypeMask int required, @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener)
+ throws FileNotFoundException {
+ if (!file.exists()) {
+ throw new FileNotFoundException(file.getAbsolutePath());
+ }
+ if (TextUtils.isEmpty(installerPackageName)) {
+ throw new FileNotFoundException(file.getAbsolutePath());
+ }
+
+ final Executor executor = mInjector.getBackgroundExecutor();
+ final Handler handler = mInjector.getBackgroundHandler();
+ final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates(
+ trustedInstallers) : null;
+
+ final List<Pair<String, File>> filesToChecksum = new ArrayList<>(1);
+ filesToChecksum.add(Pair.create(null, file));
+
+ executor.execute(() -> {
+ ApkChecksums.Injector injector = new ApkChecksums.Injector(
+ () -> mContext,
+ () -> handler,
+ () -> mInjector.getIncrementalManager(),
+ () -> mPmInternal);
+ ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
+ trustedCerts, onChecksumsReadyListener, injector);
+ });
+ }
+
private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
@Checksum.TypeMask int optional, @Checksum.TypeMask int required,
@Nullable List trustedInstallers,
@@ -1493,23 +1530,26 @@
}
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
- Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT,
- Build.VERSION.INCREMENTAL, SNAPSHOT_ENABLED);
+ PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,
+ Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL, SNAPSHOT_ENABLED);
t.traceEnd(); // "create package manager"
final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
synchronized (m.mInstallLock) {
final AndroidPackage pkg;
+ final PackageSetting ps;
final SharedUserSetting sharedUser;
final String oldSeInfo;
- final PackageStateInternal packageState = m.getPackageStateInternal(packageName);
- if (packageState == null) {
- Slog.e(TAG, "Failed to find package setting " + packageName);
- return;
+ synchronized (m.mLock) {
+ ps = m.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ Slog.e(TAG, "Failed to find package setting " + packageName);
+ return;
+ }
+ pkg = ps.getPkg();
+ sharedUser = ps.getSharedUser();
+ oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
}
- pkg = packageState.getPkg();
- sharedUser = packageState.getSharedUser();
- oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, packageState);
if (pkg == null) {
Slog.e(TAG, "Failed to find package " + packageName);
@@ -1521,7 +1561,7 @@
if (!newSeInfo.equals(oldSeInfo)) {
Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+ oldSeInfo + " to: " + newSeInfo);
- packageState.getTransientState().setOverrideSeInfo(newSeInfo);
+ ps.getPkgState().setOverrideSeInfo(newSeInfo);
m.mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
}
}
@@ -1904,8 +1944,8 @@
mIsUpgrade =
!buildFingerprint.equals(ver.fingerprint);
if (mIsUpgrade) {
- PackageManagerServiceUtils.logCriticalInfo(Log.INFO,
- "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT);
+ PackageManagerServiceUtils.logCriticalInfo(Log.INFO, "Upgrading from "
+ + ver.fingerprint + " to " + PackagePartitions.FINGERPRINT);
}
// when upgrading from pre-M, promote system app permissions from install to runtime
@@ -2003,7 +2043,8 @@
// this situation.
if (mIsUpgrade) {
Slog.i(TAG, "Build fingerprint changed from " + ver.fingerprint + " to "
- + Build.FINGERPRINT + "; regranting permissions for internal storage");
+ + PackagePartitions.FINGERPRINT
+ + "; regranting permissions for internal storage");
}
mPermissionManager.onStorageVolumeMounted(
StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade);
@@ -2037,7 +2078,7 @@
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
}
}
- ver.fingerprint = Build.FINGERPRINT;
+ ver.fingerprint = PackagePartitions.FINGERPRINT;
}
// Legacy existing (installed before Q) non-system apps to hide
@@ -2535,8 +2576,8 @@
return mComputer.canViewInstantApps(callingUid, userId);
}
- private PackageInfo generatePackageInfo(@NonNull PackageStateInternal ps, int flags,
- int userId) {
+ private PackageInfo generatePackageInfo(@NonNull PackageStateInternal ps,
+ @PackageManager.PackageInfoFlags long flags, int userId) {
return mComputer.generatePackageInfo(ps, flags, userId);
}
@@ -2575,13 +2616,14 @@
}
@Override
- public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+ public PackageInfo getPackageInfo(String packageName,
+ @PackageManager.PackageInfoFlags long flags, int userId) {
return mComputer.getPackageInfo(packageName, flags, userId);
}
@Override
public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
- int flags, int userId) {
+ @PackageManager.PackageInfoFlags long flags, int userId) {
return mComputer.getPackageInfoInternal(versionedPackage.getPackageName(),
versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
}
@@ -2618,7 +2660,7 @@
}
private boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
- int userId, int flags) {
+ int userId, @PackageManager.ComponentInfoFlags long flags) {
return mComputer.filterSharedLibPackage(ps, uid, userId, flags);
}
@@ -2633,16 +2675,19 @@
}
@Override
- public int getPackageUid(@NonNull String packageName, int flags, @UserIdInt int userId) {
+ public int getPackageUid(@NonNull String packageName,
+ @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
return mComputer.getPackageUid(packageName, flags, userId);
}
- private int getPackageUidInternal(String packageName, int flags, int userId, int callingUid) {
+ private int getPackageUidInternal(String packageName,
+ @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
return mComputer.getPackageUidInternal(packageName, flags, userId, callingUid);
}
@Override
- public int[] getPackageGids(String packageName, int flags, int userId) {
+ public int[] getPackageGids(String packageName, @PackageManager.PackageInfoFlags long flags,
+ int userId) {
return mComputer.getPackageGids(packageName, flags, userId);
}
@@ -2655,14 +2700,15 @@
.getPermissionGroupInfo(groupName, flags);
}
- private ApplicationInfo generateApplicationInfoFromSettings(String packageName, int flags,
- int filterCallingUid, int userId) {
- return mComputer.generateApplicationInfoFromSettings(packageName, flags,
- filterCallingUid, userId);
+ private ApplicationInfo generateApplicationInfoFromSettings(String packageName,
+ @PackageManager.ApplicationInfoFlags long flags, int filterCallingUid, int userId) {
+ return mComputer.generateApplicationInfoFromSettings(packageName, flags, filterCallingUid,
+ userId);
}
@Override
- public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
+ public ApplicationInfo getApplicationInfo(String packageName,
+ @PackageManager.ApplicationInfoFlags long flags, int userId) {
return mComputer.getApplicationInfo(packageName, flags, userId);
}
@@ -2672,7 +2718,8 @@
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
- private ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
+ private ApplicationInfo getApplicationInfoInternal(String packageName,
+ @PackageManager.ApplicationInfoFlags long flags,
int filterCallingUid, int userId) {
return mComputer.getApplicationInfoInternal(packageName, flags,
filterCallingUid, userId);
@@ -2907,21 +2954,21 @@
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
- private int updateFlagsForPackage(int flags, int userId) {
+ private long updateFlagsForPackage(long flags, int userId) {
return mComputer.updateFlagsForPackage(flags, userId);
}
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
- private int updateFlagsForApplication(int flags, int userId) {
+ private long updateFlagsForApplication(long flags, int userId) {
return mComputer.updateFlagsForApplication(flags, userId);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
- private int updateFlagsForComponent(int flags, int userId) {
+ private long updateFlagsForComponent(long flags, int userId) {
return mComputer.updateFlagsForComponent(flags, userId);
}
@@ -2937,7 +2984,7 @@
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- int updateFlagsForResolve(int flags, int userId, int callingUid,
+ long updateFlagsForResolve(long flags, int userId, int callingUid,
boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
return mComputer.updateFlagsForResolve(flags, userId, callingUid,
wantInstantApps, isImplicitImageCaptureIntentAndNotSetByDpc);
@@ -2949,7 +2996,8 @@
}
@Override
- public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
+ public ActivityInfo getActivityInfo(ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, int userId) {
return mComputer.getActivityInfo(component, flags, userId);
}
@@ -2959,8 +3007,8 @@
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
- private ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
- int filterCallingUid, int userId) {
+ private ActivityInfo getActivityInfoInternal(ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, int filterCallingUid, int userId) {
return mComputer.getActivityInfoInternal(component, flags,
filterCallingUid, userId);
}
@@ -2973,40 +3021,43 @@
}
@Override
- public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
+ public ActivityInfo getReceiverInfo(ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, int userId) {
return mComputer.getReceiverInfo(component, flags, userId);
}
@Override
public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(String packageName,
- int flags, int userId) {
+ @PackageManager.PackageInfoFlags long flags, int userId) {
return mComputer.getSharedLibraries(packageName, flags, userId);
}
@Nullable
@Override
public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
- @NonNull String packageName, int flags, @UserIdInt int userId) {
+ @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+ @NonNull int userId) {
return mComputer.getDeclaredSharedLibraries(packageName, flags, userId);
}
@Nullable
List<VersionedPackage> getPackagesUsingSharedLibrary(
- SharedLibraryInfo libInfo, int flags, int callingUid, int userId) {
+ SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlags long flags, int callingUid,
+ int userId) {
return mComputer.getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
}
@Nullable
@Override
- public ServiceInfo getServiceInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId) {
+ public ServiceInfo getServiceInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
return mComputer.getServiceInfo(component, flags, userId);
}
@Nullable
@Override
- public ProviderInfo getProviderInfo(@NonNull ComponentName component, int flags,
- @UserIdInt int userId) {
+ public ProviderInfo getProviderInfo(@NonNull ComponentName component,
+ @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
return mComputer.getProviderInfo(component, flags, userId);
}
@@ -3295,7 +3346,7 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int userId) {
+ @PackageManager.ResolveInfoFlags long flags, int userId) {
return mResolveIntentHelper.resolveIntentInternal(intent, resolvedType, flags,
0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
}
@@ -3340,7 +3391,7 @@
*/
@GuardedBy("mLock")
boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
- String resolvedType, int flags) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
return mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
resolvedType, flags);
}
@@ -3348,10 +3399,10 @@
@GuardedBy("mLock")
ResolveInfo findPersistentPreferredActivityLP(Intent intent,
String resolvedType,
- int flags, List<ResolveInfo> query, boolean debug, int userId) {
+ @PackageManager.ResolveInfoFlags long flags, List<ResolveInfo> query, boolean debug,
+ int userId) {
return mComputer.findPersistentPreferredActivityLP(intent,
- resolvedType,
- flags, query, debug, userId);
+ resolvedType, flags, query, debug, userId);
}
// findPreferredActivityBody returns two items: a "things changed" flag and a
@@ -3362,7 +3413,7 @@
}
FindPreferredActivityBodyResult findPreferredActivityInternal(
- Intent intent, String resolvedType, int flags,
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
return mComputer.findPreferredActivityInternal(
@@ -3392,7 +3443,7 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
@@ -3412,21 +3463,23 @@
}
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
return mComputer.queryIntentActivitiesInternal(intent,
resolvedType, flags, userId);
}
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
- int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ @PrivateResolveFlags long privateResolveFlags, int filterCallingUid, int userId,
+ boolean resolveForStart, boolean allowDynamicSplits) {
return mComputer.queryIntentActivitiesInternal(intent,
resolvedType, flags, privateResolveFlags,
filterCallingUid, userId, resolveForStart, allowDynamicSplits);
}
private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
- String resolvedType, int flags, int sourceUserId, int parentUserId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+ int parentUserId) {
return mComputer.getCrossProfileDomainPreferredLpr(intent,
resolvedType, flags, sourceUserId, parentUserId);
}
@@ -3453,20 +3506,21 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
caller, specifics, specificTypes, intent, resolvedType, flags, userId));
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(intent,
resolvedType, flags, userId, Binder.getCallingUid()));
}
@Override
- public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
+ public ResolveInfo resolveService(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlags long flags, int userId) {
final int callingUid = Binder.getCallingUid();
return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
callingUid);
@@ -3474,15 +3528,15 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentServices(Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
final int callingUid = Binder.getCallingUid();
return new ParceledListSlice<>(queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/));
}
@NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
- String resolvedType, int flags, int userId, int callingUid,
- boolean includeInstantApps) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ int callingUid, boolean includeInstantApps) {
return mComputer.queryIntentServicesInternal(intent,
resolvedType, flags, userId, callingUid,
includeInstantApps);
@@ -3490,24 +3544,27 @@
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
intent, resolvedType, flags, userId));
}
@Override
- public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
+ public ParceledListSlice<PackageInfo> getInstalledPackages(
+ @PackageManager.PackageInfoFlags long flags, int userId) {
return mComputer.getInstalledPackages(flags, userId);
}
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
- @NonNull String[] permissions, int flags, @UserIdInt int userId) {
+ @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+ @UserIdInt int userId) {
return mComputer.getPackagesHoldingPermissions(permissions, flags, userId);
}
@Override
- public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) {
+ public ParceledListSlice<ApplicationInfo> getInstalledApplications(
+ @PackageManager.ApplicationInfoFlags long flags, int userId) {
final int callingUid = Binder.getCallingUid();
return new ParceledListSlice<>(
mComputer.getInstalledApplications(flags, userId, callingUid));
@@ -3611,7 +3668,8 @@
}
@Override
- public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+ public ProviderInfo resolveContentProvider(String name,
+ @PackageManager.ResolveInfoFlags long flags, int userId) {
return mComputer.resolveContentProvider(name, flags, userId, Binder.getCallingUid());
}
@@ -3623,7 +3681,7 @@
@NonNull
@Override
public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
- int uid, int flags, @Nullable String metaDataKey) {
+ int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
return mComputer.queryContentProviders(processName, uid, flags, metaDataKey);
}
@@ -3976,7 +4034,13 @@
// - Package manager is in a state where package isn't scanned yet. This will
// get called again after scanning to fix the dependencies.
if (AndroidPackageUtils.isLibrary(pkg)) {
- if (pkg.getStaticSharedLibName() != null) {
+ if (pkg.getSdkLibName() != null) {
+ SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
+ pkg.getSdkLibName(), pkg.getSdkLibVersionMajor());
+ if (definedLibrary != null) {
+ action.accept(definedLibrary, libInfo);
+ }
+ } else if (pkg.getStaticSharedLibName() != null) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
if (definedLibrary != null) {
@@ -4128,7 +4192,9 @@
&& !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames())
&& !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames())
&& !ArrayUtils.contains(pkg.getUsesStaticLibraries(),
- changingPkg.getStaticSharedLibName())) {
+ changingPkg.getStaticSharedLibName())
+ && !ArrayUtils.contains(pkg.getUsesSdkLibraries(),
+ changingPkg.getSdkLibName())) {
continue;
}
if (resultList == null) {
@@ -4419,15 +4485,24 @@
Slog.w(TAG, "Cannot hide package: android");
return false;
}
- // Cannot hide static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null && pkg.getStaticSharedLibName() != null) {
- Slog.w(TAG, "Cannot hide package: " + packageName
- + " providing static shared library: "
- + pkg.getStaticSharedLibName());
- return false;
+ if (pkg != null) {
+ // Cannot hide SDK libs as they are controlled by SDK manager.
+ if (pkg.getSdkLibName() != null) {
+ Slog.w(TAG, "Cannot hide package: " + packageName
+ + " providing SDK library: "
+ + pkg.getSdkLibName());
+ return false;
+ }
+ // Cannot hide static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ if (pkg.getStaticSharedLibName() != null) {
+ Slog.w(TAG, "Cannot hide package: " + packageName
+ + " providing static shared library: "
+ + pkg.getStaticSharedLibName());
+ return false;
+ }
}
// Only allow protected packages to hide themselves.
if (hidden && !UserHandle.isSameApp(callingUid, pkgSetting.getAppId())
@@ -5096,15 +5171,24 @@
continue;
}
- // Cannot suspend static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null && pkg.isStaticSharedLibrary()) {
- Slog.w(TAG, "Cannot suspend package: " + packageName
- + " providing static shared library: "
- + pkg.getStaticSharedLibName());
- continue;
+ if (pkg != null) {
+ // Cannot suspend SDK libs as they are controlled by SDK manager.
+ if (pkg.isSdkLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing SDK library: "
+ + pkg.getSdkLibName());
+ continue;
+ }
+ // Cannot suspend static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ if (pkg.isStaticSharedLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing static shared library: "
+ + pkg.getStaticSharedLibName());
+ continue;
+ }
}
}
if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
@@ -5554,14 +5638,22 @@
android.Manifest.permission.DELETE_PACKAGES, null);
// TODO (b/157774108): This should fail on non-existent packages.
synchronized (mLock) {
- // Cannot block uninstall of static shared libs as they are
- // considered a part of the using app (emulating static linking).
- // Also static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null && pkg.getStaticSharedLibName() != null) {
- Slog.w(TAG, "Cannot block uninstall of package: " + packageName
- + " providing static shared library: " + pkg.getStaticSharedLibName());
- return false;
+ if (pkg != null) {
+ // Cannot block uninstall SDK libs as they are controlled by SDK manager.
+ if (pkg.getSdkLibName() != null) {
+ Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+ + " providing SDK library: " + pkg.getSdkLibName());
+ return false;
+ }
+ // Cannot block uninstall of static shared libs as they are
+ // considered a part of the using app (emulating static linking).
+ // Also static libs are installed always on internal storage.
+ if (pkg.getStaticSharedLibName() != null) {
+ Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+ + " providing static shared library: " + pkg.getStaticSharedLibName());
+ return false;
+ }
}
mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
mSettings.writePackageRestrictionsLPr(userId);
@@ -6848,24 +6940,24 @@
}
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
true /* checkShell */, "stop package");
- boolean shouldUnhibernate = false;
// writer
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.getStopped(userId) && !stopped) {
- shouldUnhibernate = true;
- }
if (!shouldFilterApplication(ps, callingUid, userId)
&& mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
- if (shouldUnhibernate) {
+ // If this would cause the app to leave force-stop, then also make sure to unhibernate the
+ // app if needed.
+ if (!stopped) {
mHandler.post(() -> {
AppHibernationManagerInternal ah =
mInjector.getLocalService(AppHibernationManagerInternal.class);
- ah.setHibernatingForUser(packageName, userId, false);
- ah.setHibernatingGlobally(packageName, false);
+ if (ah != null && ah.isHibernatingForUser(packageName, userId)) {
+ ah.setHibernatingForUser(packageName, userId, false);
+ ah.setHibernatingGlobally(packageName, false);
+ }
});
}
}
@@ -7468,8 +7560,8 @@
private class PackageManagerInternalImpl extends PackageManagerInternal {
@Override
- public List<ApplicationInfo> getInstalledApplications(int flags, int userId,
- int callingUid) {
+ public List<ApplicationInfo> getInstalledApplications(
+ @PackageManager.ApplicationInfoFlags long flags, int userId, int callingUid) {
return PackageManagerService.this.mComputer.getInstalledApplications(flags, userId,
callingUid);
}
@@ -7664,7 +7756,8 @@
@Override
public PackageInfo getPackageInfo(
- String packageName, int flags, int filterCallingUid, int userId) {
+ String packageName, @PackageManager.PackageInfoFlags long flags,
+ int filterCallingUid, int userId) {
return PackageManagerService.this.mComputer
.getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, filterCallingUid, userId);
@@ -7792,28 +7885,32 @@
}
@Override
- public int getPackageUid(String packageName, int flags, int userId) {
+ public int getPackageUid(String packageName, @PackageManager.PackageInfoFlags long flags,
+ int userId) {
return PackageManagerService.this
.getPackageUidInternal(packageName, flags, userId, Process.SYSTEM_UID);
}
@Override
public ApplicationInfo getApplicationInfo(
- String packageName, int flags, int filterCallingUid, int userId) {
+ String packageName, @PackageManager.ApplicationInfoFlags long flags,
+ int filterCallingUid, int userId) {
return PackageManagerService.this
.getApplicationInfoInternal(packageName, flags, filterCallingUid, userId);
}
@Override
public ActivityInfo getActivityInfo(
- ComponentName component, int flags, int filterCallingUid, int userId) {
+ ComponentName component, @PackageManager.ComponentInfoFlags long flags,
+ int filterCallingUid, int userId) {
return PackageManagerService.this
.getActivityInfoInternal(component, flags, filterCallingUid, userId);
}
@Override
public List<ResolveInfo> queryIntentActivities(
- Intent intent, String resolvedType, int flags, int filterCallingUid, int userId) {
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ int filterCallingUid, int userId) {
return PackageManagerService.this
.queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
@@ -7821,14 +7918,16 @@
@Override
public List<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, int flags, int filterCallingUid, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ int filterCallingUid, int userId) {
return PackageManagerService.this.mResolveIntentHelper.queryIntentReceiversInternal(
intent, resolvedType, flags, userId, filterCallingUid);
}
@Override
public List<ResolveInfo> queryIntentServices(
- Intent intent, int flags, int callingUid, int userId) {
+ Intent intent, @PackageManager.ResolveInfoFlags long flags, int callingUid,
+ int userId) {
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
return PackageManagerService.this
.queryIntentServicesInternal(intent, resolvedType, flags, userId, callingUid,
@@ -7894,7 +7993,7 @@
}
@Override
- public boolean isEnabledAndMatches(ParsedMainComponent component, int flags, int userId) {
+ public boolean isEnabledAndMatches(ParsedMainComponent component, long flags, int userId) {
return PackageStateUtils.isEnabledAndMatches(
getPackageStateInternal(component.getPackageName()), component, flags, userId);
}
@@ -8096,8 +8195,9 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int privateResolveFlags, int userId, boolean resolveForStart,
- int filterCallingUid) {
+ @PackageManager.ResolveInfoFlags long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
+ boolean resolveForStart, int filterCallingUid) {
return mResolveIntentHelper.resolveIntentInternal(
intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
filterCallingUid);
@@ -8105,14 +8205,14 @@
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType,
- int flags, int userId, int callingUid) {
+ @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
callingUid);
}
@Override
- public ProviderInfo resolveContentProvider(String name, int flags, int userId,
- int callingUid) {
+ public ProviderInfo resolveContentProvider(String name,
+ @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
return PackageManagerService.this.mComputer
.resolveContentProvider(name, flags, userId,callingUid);
}
@@ -8996,9 +9096,12 @@
@Override
public void setSplashScreenTheme(@NonNull String packageName, @Nullable String themeId,
int userId) {
- mutateInstalledPackageSetting(packageName, Binder.getCallingUid(), userId, pkgSetting -> {
- pkgSetting.setSplashScreenTheme(userId, themeId);
- });
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "setSplashScreenTheme");
+ enforceOwnerRights(packageName, callingUid);
+ mutateInstalledPackageSetting(packageName, callingUid, userId,
+ pkgSetting -> pkgSetting.setSplashScreenTheme(userId, themeId));
}
@Override
@@ -9373,7 +9476,8 @@
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
- mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+ mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
+ | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 6d031dd..3b643b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -25,6 +25,7 @@
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
+import static com.android.server.pm.PackageManagerService.RANDOM_CODEPATH_PREFIX;
import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX;
import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -41,6 +42,7 @@
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
+import android.content.pm.PackagePartitions;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
@@ -123,6 +125,8 @@
public class PackageManagerServiceUtils {
private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 3 * 1000 * 1000; // 3MB
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE;
+
public final static Predicate<PackageStateInternal> REMOVE_IF_NULL_PKG =
pkgSetting -> pkgSetting.getPkg() == null;
@@ -1075,7 +1079,7 @@
public static boolean hasAnyDomainApproval(
@NonNull DomainVerificationManagerInternal manager,
@NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
- @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
+ @PackageManager.ResolveInfoFlags long resolveInfoFlags, @UserIdInt int userId) {
return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
> DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
}
@@ -1122,13 +1126,28 @@
File firstLevelDir;
do {
random.nextBytes(bytes);
- String dirName = RANDOM_DIR_PREFIX
+ String firstLevelDirName = RANDOM_DIR_PREFIX
+ Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
- firstLevelDir = new File(targetDir, dirName);
+ firstLevelDir = new File(targetDir, firstLevelDirName);
} while (firstLevelDir.exists());
+
random.nextBytes(bytes);
- String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
- return new File(firstLevelDir, packageName + "-" + suffix);
+ String dirName = packageName + RANDOM_CODEPATH_PREFIX + Base64.encodeToString(bytes,
+ Base64.URL_SAFE | Base64.NO_WRAP);
+ final File result = new File(firstLevelDir, dirName);
+ if (DEBUG && !Objects.equals(tryParsePackageName(result.getName()), packageName)) {
+ throw new RuntimeException(
+ "codepath is off: " + result.getName() + " (" + packageName + ")");
+ }
+ return result;
+ }
+
+ static String tryParsePackageName(@NonNull String codePath) throws IllegalArgumentException {
+ int packageNameEnds = codePath.indexOf(RANDOM_CODEPATH_PREFIX);
+ if (packageNameEnds == -1) {
+ throw new IllegalArgumentException("Not a valid package folder name");
+ }
+ return codePath.substring(0, packageNameEnds);
}
/**
@@ -1215,7 +1234,7 @@
// identify cached items. In particular, changing the value of certain
// feature flags should cause us to invalidate any caches.
final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
- : SystemProperties.digestOf("ro.build.fingerprint");
+ : PackagePartitions.FINGERPRINT;
// Reconcile cache directories, keeping only what we'd actually use.
for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index fc59541..b89017e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -49,6 +49,7 @@
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
@@ -667,6 +668,8 @@
return runListPermissions();
case "staged-sessions":
return runListStagedSessions();
+ case "sdks":
+ return runListSdks();
case "users":
ServiceManager.getService("user").shellCommand(
getInFileDescriptor(), getOutFileDescriptor(), getErrFileDescriptor(),
@@ -792,6 +795,15 @@
}
private int runListPackages(boolean showSourceDir) throws RemoteException {
+ return runListPackages(showSourceDir, false);
+ }
+
+ private int runListSdks() throws RemoteException {
+ return runListPackages(false, true);
+ }
+
+ private int runListPackages(boolean showSourceDir, boolean showSdks) throws RemoteException {
+ final String prefix = showSdks ? "sdk:" : "package:";
final PrintWriter pw = getOutPrintWriter();
int getFlags = 0;
boolean listDisabled = false, listEnabled = false;
@@ -885,37 +897,61 @@
}
final boolean isSystem = !isApex &&
- (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
final boolean isEnabled = !isApex && info.applicationInfo.enabled;
- if ((!listDisabled || !isEnabled) &&
- (!listEnabled || isEnabled) &&
- (!listSystem || isSystem) &&
- (!listThirdParty || !isSystem) &&
- (!listApexOnly || isApex)) {
- pw.print("package:");
- if (showSourceDir) {
- pw.print(info.applicationInfo.sourceDir);
- pw.print("=");
+ if ((listDisabled && isEnabled) ||
+ (listEnabled && !isEnabled) ||
+ (listSystem && !isSystem) ||
+ (listThirdParty && isSystem) ||
+ (listApexOnly && !isApex)) {
+ continue;
+ }
+
+ String name = null;
+ if (showSdks) {
+ final ParceledListSlice<SharedLibraryInfo> libsSlice =
+ mInterface.getDeclaredSharedLibraries(info.packageName, getFlags, userId);
+ if (libsSlice == null) {
+ continue;
}
- pw.print(info.packageName);
- if (showVersionCode) {
- pw.print(" versionCode:");
- if (info.applicationInfo != null) {
- pw.print(info.applicationInfo.longVersionCode);
- } else {
- pw.print(info.getLongVersionCode());
+ final List<SharedLibraryInfo> libs = libsSlice.getList();
+ for (int l = 0, lsize = libs.size(); l < lsize; ++l) {
+ SharedLibraryInfo lib = libs.get(l);
+ if (lib.getType() == SharedLibraryInfo.TYPE_SDK) {
+ name = lib.getName() + ":" + lib.getLongVersion();
+ break;
}
}
- if (listInstaller) {
- pw.print(" installer=");
- pw.print(mInterface.getInstallerPackageName(info.packageName));
+ if (name == null) {
+ continue;
}
- if (showUid && !isApex) {
- pw.print(" uid:");
- pw.print(info.applicationInfo.uid);
- }
- pw.println();
+ } else {
+ name = info.packageName;
}
+
+ pw.print(prefix);
+ if (showSourceDir) {
+ pw.print(info.applicationInfo.sourceDir);
+ pw.print("=");
+ }
+ pw.print(name);
+ if (showVersionCode) {
+ pw.print(" versionCode:");
+ if (info.applicationInfo != null) {
+ pw.print(info.applicationInfo.longVersionCode);
+ } else {
+ pw.print(info.getLongVersionCode());
+ }
+ }
+ if (listInstaller) {
+ pw.print(" installer=");
+ pw.print(mInterface.getInstallerPackageName(info.packageName));
+ }
+ if (showUid && !isApex) {
+ pw.print(" uid:");
+ pw.print(info.applicationInfo.uid);
+ }
+ pw.println();
}
return 0;
}
@@ -2060,7 +2096,7 @@
} else {
if ((flags & PackageManager.DELETE_ALL_USERS) == 0) {
final PackageInfo info = mInterface.getPackageInfo(packageName,
- PackageManager.MATCH_STATIC_SHARED_LIBRARIES, translatedUserId);
+ PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES, translatedUserId);
if (info == null) {
pw.println("Failure [not installed for " + translatedUserId + "]");
return 1;
@@ -2451,6 +2487,15 @@
}
}
+ private String getApexPackageNameContainingPackage(String pkg) {
+ ApexManager apexManager = ApexManager.getInstance();
+ return apexManager.getActiveApexPackageNameContainingPackage(pkg);
+ }
+
+ private boolean isApexApp(String pkg) {
+ return getApexPackageNameContainingPackage(pkg) != null;
+ }
+
private int runGetPrivappPermissions() {
final String pkg = getNextArg();
if (pkg == null) {
@@ -2466,6 +2511,9 @@
} else if (isSystemExtApp(pkg)) {
privAppPermissions = SystemConfig.getInstance()
.getSystemExtPrivAppPermissions(pkg);
+ } else if (isApexApp(pkg)) {
+ privAppPermissions = SystemConfig.getInstance()
+ .getApexPrivAppPermissions(getApexPackageNameContainingPackage(pkg), pkg);
} else {
privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg);
}
@@ -2490,6 +2538,9 @@
} else if (isSystemExtApp(pkg)) {
privAppPermissions = SystemConfig.getInstance()
.getSystemExtPrivAppDenyPermissions(pkg);
+ } else if (isApexApp(pkg)) {
+ privAppPermissions = SystemConfig.getInstance()
+ .getApexPrivAppDenyPermissions(getApexPackageNameContainingPackage(pkg), pkg);
} else {
privAppPermissions = SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index dc514c1..923a133 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -32,12 +32,6 @@
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.overlay.OverlayPaths;
-
-import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.PackageUserState;
-import com.android.server.pm.pkg.PackageUserStateImpl;
-import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.SuspendParams;
import android.os.PersistableBundle;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
@@ -53,7 +47,12 @@
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUnserialized;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateImpl;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SuspendParams;
import com.android.server.utils.SnapshotCache;
import libcore.util.EmptyArray;
@@ -94,6 +93,12 @@
private Set<String> mOldCodePaths;
@Nullable
+ private String[] usesSdkLibraries;
+
+ @Nullable
+ private long[] usesSdkLibrariesVersionsMajor;
+
+ @Nullable
private String[] usesStaticLibraries;
@Nullable
@@ -208,12 +213,16 @@
String legacyNativeLibraryPath, String primaryCpuAbi,
String secondaryCpuAbi, String cpuAbiOverride,
long longVersionCode, int pkgFlags, int pkgPrivateFlags,
- int sharedUserId, String[] usesStaticLibraries,
- long[] usesStaticLibrariesVersions, Map<String, Set<String>> mimeGroups,
+ int sharedUserId,
+ String[] usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
+ String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
+ Map<String, Set<String>> mimeGroups,
@NonNull UUID domainSetId) {
super(pkgFlags, pkgPrivateFlags);
this.mName = name;
this.mRealName = realName;
+ this.usesSdkLibraries = usesSdkLibraries;
+ this.usesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor;
this.usesStaticLibraries = usesStaticLibraries;
this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
this.mPath = path;
@@ -617,6 +626,13 @@
forceQueryableOverride = other.forceQueryableOverride;
mDomainSetId = other.mDomainSetId;
+ usesSdkLibraries = other.usesSdkLibraries != null
+ ? Arrays.copyOf(other.usesSdkLibraries,
+ other.usesSdkLibraries.length) : null;
+ usesSdkLibrariesVersionsMajor = other.usesSdkLibrariesVersionsMajor != null
+ ? Arrays.copyOf(other.usesSdkLibrariesVersionsMajor,
+ other.usesSdkLibrariesVersionsMajor.length) : null;
+
usesStaticLibraries = other.usesStaticLibraries != null
? Arrays.copyOf(other.usesStaticLibraries,
other.usesStaticLibraries.length) : null;
@@ -1225,6 +1241,19 @@
@NonNull
@Override
+ public String[] getUsesSdkLibraries() {
+ return usesSdkLibraries == null ? EmptyArray.STRING : usesSdkLibraries;
+ }
+
+ @NonNull
+ @Override
+ public long[] getUsesSdkLibrariesVersionsMajor() {
+ return usesSdkLibrariesVersionsMajor == null ? EmptyArray.LONG
+ : usesSdkLibrariesVersionsMajor;
+ }
+
+ @NonNull
+ @Override
public String[] getUsesStaticLibraries() {
return usesStaticLibraries == null ? EmptyArray.STRING : usesStaticLibraries;
}
@@ -1300,6 +1329,18 @@
return this;
}
+ public PackageSetting setUsesSdkLibraries(String[] usesSdkLibraries) {
+ this.usesSdkLibraries = usesSdkLibraries;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setUsesSdkLibrariesVersionsMajor(long[] usesSdkLibrariesVersions) {
+ this.usesSdkLibrariesVersionsMajor = usesSdkLibrariesVersions;
+ onChanged();
+ return this;
+ }
+
public PackageSetting setUsesStaticLibraries(String[] usesStaticLibraries) {
this.usesStaticLibraries = usesStaticLibraries;
onChanged();
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index cb97e2a..8c91b16 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -75,8 +75,8 @@
}
private ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType,
- int flags, List<ResolveInfo> query, boolean always, boolean removeMatches,
- boolean debug, int userId) {
+ @PackageManager.ResolveInfoFlags long flags, List<ResolveInfo> query, boolean always,
+ boolean removeMatches, boolean debug, int userId) {
return findPreferredActivityNotLocked(
intent, resolvedType, flags, query, always, removeMatches, debug, userId,
UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
@@ -85,8 +85,9 @@
// TODO: handle preferred activities missing while user has amnesia
/** <b>must not hold {@link PackageManagerService.mLock}</b> */
public ResolveInfo findPreferredActivityNotLocked(
- Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean always,
- boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
+ int userId, boolean queryMayBeFiltered) {
if (Thread.holdsLock(mPm.mLock)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
@@ -675,7 +676,7 @@
final int callingUid = Binder.getCallingUid();
intent = PackageManagerServiceUtils.updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mPm.mContext.getContentResolver());
- final int flags = mPm.updateFlagsForResolve(
+ final long flags = mPm.updateFlagsForResolve(
0, userId, callingUid, false /*includeInstantApps*/,
mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
0));
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 3a2ac1c..749495c 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -94,9 +94,10 @@
}
}
- mInstaller.rmPackageDir(codePath.getAbsolutePath());
+ final String packageName = codePath.getName();
+ mInstaller.rmPackageDir(packageName, codePath.getAbsolutePath());
if (needRemoveParent) {
- mInstaller.rmPackageDir(codePathParent.getAbsolutePath());
+ mInstaller.rmPackageDir(packageName, codePathParent.getAbsolutePath());
removeCachedResult(codePathParent);
}
} catch (Installer.InstallerException e) {
@@ -188,7 +189,19 @@
r = null;
- // Any package can hold static shared libraries.
+ // Any package can hold SDK or static shared libraries.
+ if (pkg.getSdkLibName() != null) {
+ if (removeSharedLibraryLPw(pkg.getSdkLibName(), pkg.getSdkLibVersionMajor())) {
+ if (DEBUG_REMOVE && chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append(pkg.getSdkLibName());
+ }
+ }
+ }
if (pkg.getStaticSharedLibName() != null) {
if (removeSharedLibraryLPw(pkg.getStaticSharedLibName(),
pkg.getStaticSharedLibVersion())) {
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 70855a9..0ee1f89 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -74,8 +74,9 @@
* However, if {@code resolveForStart} is {@code true}, all instant apps are visible
* since we need to allow the system to start any installed application.
*/
- public ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags,
- @PackageManagerInternal.PrivateResolveFlags int privateResolveFlags, int userId,
+ public ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlags long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
boolean resolveForStart, int filterCallingUid) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
@@ -114,8 +115,9 @@
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
- int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
- boolean queryMayBeFiltered) {
+ @PackageManager.ResolveInfoFlags long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
+ List<ResolveInfo> query, int userId, boolean queryMayBeFiltered) {
if (query != null) {
final int n = query.size();
if (n == 1) {
@@ -276,7 +278,8 @@
// In this method, we have to know the actual calling UID, but in some cases Binder's
// call identity is removed, so the UID has to be passed in explicitly.
public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
- String resolvedType, int flags, int userId, int filterCallingUid) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+ int filterCallingUid) {
if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
mPm.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
false /*checkShell*/, "query intent receivers");
@@ -370,8 +373,8 @@
}
- public ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
- int userId, int callingUid) {
+ public ResolveInfo resolveServiceInternal(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
if (!mPm.mUserManager.exists(userId)) return null;
flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
@@ -388,7 +391,8 @@
}
public @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
- Intent intent, String resolvedType, int flags, int userId) {
+ Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+ int userId) {
if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = mPm.getInstantAppPackageName(callingUid);
@@ -529,7 +533,7 @@
public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
- String resolvedType, int flags, int userId) {
+ String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index 6cc94ce..eafe0d9 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -294,6 +294,12 @@
}
}
+ String[] usesSdkLibraries = null;
+ if (!parsedPackage.getUsesSdkLibraries().isEmpty()) {
+ usesSdkLibraries = new String[parsedPackage.getUsesSdkLibraries().size()];
+ parsedPackage.getUsesSdkLibraries().toArray(usesSdkLibraries);
+ }
+
String[] usesStaticLibraries = null;
if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
@@ -324,7 +330,8 @@
AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
true /*allowInstall*/, instantApp, virtualPreload,
- UserManagerService.getInstance(), usesStaticLibraries,
+ UserManagerService.getInstance(), usesSdkLibraries,
+ parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
newDomainSetId);
} else {
@@ -344,6 +351,7 @@
PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
UserManagerService.getInstance(),
+ usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
parsedPackage.getMimeGroups(), newDomainSetId);
}
@@ -552,6 +560,10 @@
pkgSetting.setVolumeUuid(volumeUuid);
}
+ SharedLibraryInfo sdkLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
+ sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
+ }
SharedLibraryInfo staticSharedLibraryInfo = null;
if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
staticSharedLibraryInfo =
@@ -568,7 +580,7 @@
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
!createNewPackage /* existingSettingCopied */,
- previousAppId, staticSharedLibraryInfo,
+ previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
dynamicSharedLibraryInfos);
}
@@ -894,14 +906,15 @@
* Returns if forced apk verification can be skipped for the whole package, including splits.
*/
private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
+ final String packageName = pkg.getPackageName();
+ if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) {
return false;
}
// TODO: Allow base and splits to be verified individually.
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
for (int i = 0; i < splitCodePaths.length; i++) {
- if (!canSkipForcedApkVerification(splitCodePaths[i])) {
+ if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) {
return false;
}
}
@@ -914,7 +927,7 @@
* whether the apk contains signed root hash. Note that the signer's certificate still needs to
* match one in a trusted source, and should be done separately.
*/
- private boolean canSkipForcedApkVerification(String apkPath) {
+ private boolean canSkipForcedApkVerification(String packageName, String apkPath) {
if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
return VerityUtils.hasFsverity(apkPath);
}
@@ -926,7 +939,8 @@
}
synchronized (mPm.mInstallLock) {
// Returns whether the observed root hash matches what kernel has.
- mPm.mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath,
+ rootHashObserved);
return true;
}
} catch (Installer.InstallerException | IOException | DigestException
diff --git a/services/core/java/com/android/server/pm/ScanResult.java b/services/core/java/com/android/server/pm/ScanResult.java
index eb44a82..f77be1f 100644
--- a/services/core/java/com/android/server/pm/ScanResult.java
+++ b/services/core/java/com/android/server/pm/ScanResult.java
@@ -51,6 +51,8 @@
/** ABI code paths that have changed in the package scan */
@Nullable public final List<String> mChangedAbiCodePath;
+ public final SharedLibraryInfo mSdkSharedLibraryInfo;
+
public final SharedLibraryInfo mStaticSharedLibraryInfo;
public final List<SharedLibraryInfo> mDynamicSharedLibraryInfos;
@@ -60,6 +62,7 @@
@Nullable PackageSetting pkgSetting,
@Nullable List<String> changedAbiCodePath, boolean existingSettingCopied,
int previousAppId,
+ SharedLibraryInfo sdkSharedLibraryInfo,
SharedLibraryInfo staticSharedLibraryInfo,
List<SharedLibraryInfo> dynamicSharedLibraryInfos) {
mRequest = request;
@@ -68,6 +71,7 @@
mChangedAbiCodePath = changedAbiCodePath;
mExistingSettingCopied = existingSettingCopied;
mPreviousAppId = previousAppId;
+ mSdkSharedLibraryInfo = sdkSharedLibraryInfo;
mStaticSharedLibraryInfo = staticSharedLibraryInfo;
mDynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4a41047..45994f6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -42,6 +42,7 @@
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackagePartitions;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
@@ -153,7 +154,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@@ -276,6 +276,7 @@
private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
private static final String TAG_PERMISSIONS = "perms";
private static final String TAG_CHILD_PACKAGE = "child-package";
+ private static final String TAG_USES_SDK_LIB = "uses-sdk-lib";
private static final String TAG_USES_STATIC_LIB = "uses-static-lib";
private static final String TAG_BLOCK_UNINSTALL_PACKAGES = "block-uninstall-packages";
private static final String TAG_BLOCK_UNINSTALL = "block-uninstall";
@@ -437,7 +438,7 @@
public void forceCurrent() {
sdkVersion = Build.VERSION.SDK_INT;
databaseVersion = CURRENT_DATABASE_VERSION;
- fingerprint = Build.FINGERPRINT;
+ fingerprint = PackagePartitions.FINGERPRINT;
}
}
@@ -826,6 +827,7 @@
p.getLegacyNativeLibraryPath(), p.getPrimaryCpuAbi(),
p.getSecondaryCpuAbi(), p.getCpuAbiOverride(),
p.getAppId(), p.getVersionCode(), p.getFlags(), p.getPrivateFlags(),
+ p.getUsesSdkLibraries(), p.getUsesSdkLibrariesVersionsMajor(),
p.getUsesStaticLibraries(), p.getUsesStaticLibrariesVersions(), p.getMimeGroups(),
mDomainVerificationManager.generateNewId());
if (ret != null) {
@@ -849,9 +851,10 @@
PackageSetting addPackageLPw(String name, String realName, File codePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
- String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
- pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
- long[] usesStaticLibraryNames, Map<String, Set<String>> mimeGroups,
+ String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc,
+ int pkgFlags, int pkgPrivateFlags, String[] usesSdkLibraries,
+ long[] usesSdkLibrariesVersions, String[] usesStaticLibraries,
+ long[] usesStaticLibrariesVersions, Map<String, Set<String>> mimeGroups,
@NonNull UUID domainSetId) {
PackageSetting p = mPackages.get(name);
if (p != null) {
@@ -864,8 +867,8 @@
}
p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
- pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
- mimeGroups, domainSetId);
+ pkgPrivateFlags, 0 /*userId*/, usesSdkLibraries, usesSdkLibrariesVersions,
+ usesStaticLibraries, usesStaticLibrariesVersions, mimeGroups, domainSetId);
p.setAppId(uid);
if (registerExistingAppIdLPw(uid, p, name)) {
mPackages.put(name, p);
@@ -925,6 +928,7 @@
String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
UserHandle installUser, boolean allowInstall, boolean instantApp,
boolean virtualPreload, UserManagerService userManager,
+ String[] usesSdkLibraries, long[] usesSdkLibrariesVersions,
String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
Set<String> mimeGroupNames, @NonNull UUID domainSetId) {
final PackageSetting pkgSetting;
@@ -940,6 +944,8 @@
// overwrite the signatures in the original package setting.
.setSignatures(new PackageSignatures())
.setLongVersionCode(versionCode)
+ .setUsesSdkLibraries(usesSdkLibraries)
+ .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions)
.setUsesStaticLibraries(usesStaticLibraries)
.setUsesStaticLibrariesVersions(usesStaticLibrariesVersions)
// Update new package state.
@@ -951,8 +957,9 @@
pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
- 0 /*sharedUserId*/, usesStaticLibraries,
- usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames), domainSetId);
+ 0 /*sharedUserId*/, usesSdkLibraries, usesSdkLibrariesVersions,
+ usesStaticLibraries, usesStaticLibrariesVersions,
+ createMimeGroups(mimeGroupNames), domainSetId);
pkgSetting.setLastModifiedTime(codePath.lastModified());
pkgSetting.setSharedUser(sharedUser);
// If this is not a system app, it starts out stopped.
@@ -1046,6 +1053,7 @@
@NonNull File codePath, @Nullable String legacyNativeLibraryPath,
@Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
int pkgPrivateFlags, @NonNull UserManagerService userManager,
+ @Nullable String[] usesSdkLibraries, @Nullable long[] usesSdkLibrariesVersions,
@Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
@Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
throws PackageManagerException {
@@ -1095,7 +1103,17 @@
.setSecondaryCpuAbi(secondaryCpuAbi)
.updateMimeGroups(mimeGroupNames)
.setDomainSetId(domainSetId);
- // Update static shared library dependencies if needed
+ // Update SDK library dependencies if needed.
+ if (usesSdkLibraries != null && usesSdkLibrariesVersions != null
+ && usesSdkLibraries.length == usesSdkLibrariesVersions.length) {
+ pkgSetting.setUsesSdkLibraries(usesSdkLibraries)
+ .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions);
+ } else {
+ pkgSetting.setUsesSdkLibraries(null)
+ .setUsesSdkLibrariesVersionsMajor(null);
+ }
+
+ // Update static shared library dependencies if needed.
if (usesStaticLibraries != null && usesStaticLibrariesVersions != null
&& usesStaticLibraries.length == usesStaticLibrariesVersions.length) {
pkgSetting.setUsesStaticLibraries(usesStaticLibraries)
@@ -2167,6 +2185,21 @@
}
}
+ void readUsesSdkLibLPw(TypedXmlPullParser parser, PackageSetting outPs)
+ throws IOException, XmlPullParserException {
+ String libName = parser.getAttributeValue(null, ATTR_NAME);
+ long libVersion = parser.getAttributeLong(null, ATTR_VERSION, -1);
+
+ if (libName != null && libVersion >= 0) {
+ outPs.setUsesSdkLibraries(ArrayUtils.appendElement(String.class,
+ outPs.getUsesSdkLibraries(), libName));
+ outPs.setUsesSdkLibrariesVersionsMajor(ArrayUtils.appendLong(
+ outPs.getUsesSdkLibrariesVersionsMajor(), libVersion));
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+ }
+
void readUsesStaticLibLPw(TypedXmlPullParser parser, PackageSetting outPs)
throws IOException, XmlPullParserException {
String libName = parser.getAttributeValue(null, ATTR_NAME);
@@ -2182,6 +2215,23 @@
XmlUtils.skipCurrentTag(parser);
}
+ void writeUsesSdkLibLPw(TypedXmlSerializer serializer, String[] usesSdkLibraries,
+ long[] usesSdkLibraryVersions) throws IOException {
+ if (ArrayUtils.isEmpty(usesSdkLibraries) || ArrayUtils.isEmpty(usesSdkLibraryVersions)
+ || usesSdkLibraries.length != usesSdkLibraryVersions.length) {
+ return;
+ }
+ final int libCount = usesSdkLibraries.length;
+ for (int i = 0; i < libCount; i++) {
+ final String libName = usesSdkLibraries[i];
+ final long libVersion = usesSdkLibraryVersions[i];
+ serializer.startTag(null, TAG_USES_SDK_LIB);
+ serializer.attribute(null, ATTR_NAME, libName);
+ serializer.attributeLong(null, ATTR_VERSION, libVersion);
+ serializer.endTag(null, TAG_USES_SDK_LIB);
+ }
+ }
+
void writeUsesStaticLibLPw(TypedXmlSerializer serializer, String[] usesStaticLibraries,
long[] usesStaticLibraryVersions) throws IOException {
if (ArrayUtils.isEmpty(usesStaticLibraries) || ArrayUtils.isEmpty(usesStaticLibraryVersions)
@@ -2707,6 +2757,9 @@
}
serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
+ writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
+ pkg.getUsesSdkLibrariesVersionsMajor());
+
writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions());
@@ -2785,6 +2838,9 @@
serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
+ writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
+ pkg.getUsesSdkLibrariesVersionsMajor());
+
writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions());
@@ -3455,8 +3511,8 @@
UUID domainSetId = DomainVerificationManagerInternal.DISABLED_ID;
PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr,
- versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null,
- domainSetId);
+ versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null, null,
+ null, domainSetId);
long timeStamp = parser.getAttributeLongHex(null, "ft", 0);
if (timeStamp == 0) {
timeStamp = parser.getAttributeLong(null, "ts", 0);
@@ -3484,6 +3540,8 @@
readInstallPermissionsLPr(parser, ps.getLegacyPermissionState(), users);
} else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
readUsesStaticLibLPw(parser, ps);
+ } else if (parser.getName().equals(TAG_USES_SDK_LIB)) {
+ readUsesSdkLibLPw(parser, ps);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <updated-package>: " + parser.getName());
@@ -3642,8 +3700,9 @@
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
- null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/,
- null /*mimeGroups*/, domainSetId);
+ null /* usesSdkLibraries */, null /* usesSdkLibraryVersions */,
+ null /* usesStaticLibraries */, null /* usesStaticLibraryVersions */,
+ null /* mimeGroups */, domainSetId);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ userId + " pkg=" + packageSetting);
@@ -3662,9 +3721,11 @@
new File(codePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
- null /*usesStaticLibraries*/,
- null /*usesStaticLibraryVersions*/,
- null /*mimeGroups*/, domainSetId);
+ null /* usesSdkLibraries */,
+ null /* usesSdkLibrariesVersions */,
+ null /* usesStaticLibraries */,
+ null /* usesStaticLibraryVersions */,
+ null /* mimeGroups */, domainSetId);
packageSetting.setLastModifiedTime(timeStamp);
packageSetting.setFirstInstallTime(firstInstallTime);
packageSetting.setLastUpdateTime(lastUpdateTime);
@@ -3793,6 +3854,8 @@
}
} else if (tagName.equals(TAG_USES_STATIC_LIB)) {
readUsesStaticLibLPw(parser, packageSetting);
+ } else if (tagName.equals(TAG_USES_SDK_LIB)) {
+ readUsesSdkLibLPw(parser, packageSetting);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <package>: " + parser.getName());
@@ -4149,7 +4212,7 @@
return getDisabledSystemPkgLPr(enabledPackageSetting.getPackageName());
}
- boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
+ boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, long flags, int userId) {
final PackageSetting ps = mPackages.get(componentInfo.packageName);
if (ps == null) return false;
@@ -4159,7 +4222,7 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean isEnabledAndMatchLPr(AndroidPackage pkg, ParsedMainComponent component,
- int flags, int userId) {
+ long flags, int userId) {
final PackageSetting ps = mPackages.get(component.getPackageName());
if (ps == null) return false;
@@ -4581,6 +4644,13 @@
pw.print(" version:"); pw.println(pkg.getStaticSharedLibVersion());
}
+ if (pkg.getSdkLibName() != null) {
+ pw.print(prefix); pw.println(" SDK library:");
+ pw.print(prefix); pw.print(" ");
+ pw.print("name:"); pw.print(pkg.getSdkLibName());
+ pw.print(" versionMajor:"); pw.println(pkg.getSdkLibVersionMajor());
+ }
+
List<String> usesLibraries = pkg.getUsesLibraries();
if (usesLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesLibraries:");
@@ -4600,6 +4670,17 @@
}
}
+ List<String> usesSdkLibraries = pkg.getUsesSdkLibraries();
+ long[] usesSdkLibrariesVersionsMajor = pkg.getUsesSdkLibrariesVersionsMajor();
+ if (usesSdkLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesSdkLibraries:");
+ for (int i = 0, size = usesSdkLibraries.size(); i < size; ++i) {
+ pw.print(prefix); pw.print(" ");
+ pw.print(usesSdkLibraries.get(i)); pw.print(" version:");
+ pw.println(usesSdkLibrariesVersionsMajor[i]);
+ }
+ }
+
List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
if (usesOptionalLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesOptionalLibraries:");
@@ -5357,7 +5438,7 @@
}
private String getExtendedFingerprint(long version) {
- return Build.FINGERPRINT + "?pc_version=" + version;
+ return PackagePartitions.FINGERPRINT + "?pc_version=" + version;
}
public void writeStateForUserAsyncLPr(int userId) {
diff --git a/services/core/java/com/android/server/pm/SharedLibraryHelper.java b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
index 461fca1..dd8fad0 100644
--- a/services/core/java/com/android/server/pm/SharedLibraryHelper.java
+++ b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
@@ -76,12 +76,15 @@
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
// Let's used the parsed package as scanResult.pkgSetting may be null
final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
- if (scanResult.mStaticSharedLibraryInfo == null
+ if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null
&& scanResult.mDynamicSharedLibraryInfos == null) {
return null;
}
- // Any app can add new static shared libraries
+ // Any app can add new SDKs and static shared libraries.
+ if (scanResult.mSdkSharedLibraryInfo != null) {
+ return Collections.singletonList(scanResult.mSdkSharedLibraryInfo);
+ }
if (scanResult.mStaticSharedLibraryInfo != null) {
return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
}
@@ -181,41 +184,49 @@
ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
if (!pkg.getUsesLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
- pkg.getPackageName(), true, pkg.getTargetSdkVersion(), null,
+ pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
availablePackages, existingLibraries, newLibraries);
}
if (!pkg.getUsesStaticLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
- pkg.getPackageName(), true, pkg.getTargetSdkVersion(), usesLibraryInfos,
- availablePackages, existingLibraries, newLibraries);
+ pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
if (!pkg.getUsesOptionalLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(),
- null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
+ pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
pkg.getPackageName(), pkg.getTargetSdkVersion())) {
if (!pkg.getUsesNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
- null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ null, pkg.getPackageName(), "native shared", true,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+ existingLibraries, newLibraries);
}
if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
- null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ null, null, pkg.getPackageName(), "native shared", false,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+ existingLibraries, newLibraries);
}
}
+ if (!pkg.getUsesSdkLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
+ pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
+ pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
+ availablePackages, existingLibraries, newLibraries);
+ }
return usesLibraryInfos;
}
public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
@NonNull List<String> requestedLibraries,
@Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
- @NonNull String packageName, boolean required, int targetSdk,
- @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
+ @NonNull String packageName, @NonNull String libraryType, boolean required,
+ int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
@NonNull final Map<String, AndroidPackage> availablePackages,
@NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
@Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
@@ -230,18 +241,17 @@
if (libraryInfo == null) {
if (required) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable shared library "
- + libName + "; failing!");
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library " + libName + "; failing!");
} else if (DEBUG_SHARED_LIBRARIES) {
- Slog.i(TAG, "Package " + packageName
- + " desires unavailable shared library "
- + libName + "; ignoring!");
+ Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
+ + " library " + libName + "; ignoring!");
}
} else {
if (requiredVersions != null && requiredCertDigests != null) {
if (libraryInfo.getLongVersion() != requiredVersions[i]) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable static shared"
+ "Package " + packageName + " requires unavailable " + libraryType
+ " library " + libName + " version "
+ libraryInfo.getLongVersion() + "; failing!");
}
@@ -249,7 +259,7 @@
SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
if (libPkg == null) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable static shared"
+ "Package " + packageName + " requires unavailable " + libraryType
+ " library; failing!");
}
final String[] expectedCertDigests = requiredCertDigests[i];
@@ -267,8 +277,8 @@
// Therefore, the size check is safe to make.
if (expectedCertDigests.length != libCertDigests.length) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed"
- + " static shared library; failing!");
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
}
// Use a predictable order as signature order may vary
@@ -280,8 +290,8 @@
if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
throw new PackageManagerException(
INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed"
- + " static shared library; failing!");
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
}
}
} else {
@@ -290,10 +300,9 @@
byte[] digestBytes = HexEncoding.decode(
expectedCertDigests[0], false /* allowSingleChar */);
if (!libPkg.hasSha256Certificate(digestBytes)) {
- throw new PackageManagerException(
- INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed"
- + " static shared library; failing!");
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
}
}
}
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 6b88081..1433abd 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -29,10 +29,10 @@
import android.app.ResourcesManager;
import android.content.IIntentReceiver;
import android.content.pm.PackageManager;
+import android.content.pm.PackagePartitions;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.parsing.ParsingPackageUtils;
-import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.UserHandle;
@@ -157,7 +157,7 @@
Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage());
}
- if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
+ if (!PackagePartitions.FINGERPRINT.equals(ver.fingerprint)) {
appDataHelper.clearAppDataLIF(
ps.getPkg(), UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
@@ -195,10 +195,10 @@
}
synchronized (mPm.mLock) {
- final boolean isUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
+ final boolean isUpgrade = !PackagePartitions.FINGERPRINT.equals(ver.fingerprint);
if (isUpgrade) {
logCriticalInfo(Log.INFO, "Build fingerprint changed from " + ver.fingerprint
- + " to " + Build.FINGERPRINT + "; regranting permissions for "
+ + " to " + PackagePartitions.FINGERPRINT + "; regranting permissions for "
+ volumeUuid);
}
mPm.mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade);
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index d54acb7..4bcc2a3 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -24,20 +24,6 @@
"name": "CtsMatchFlagTestCases"
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.pm."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "FrameworksMockingServicesTests",
"options": [
{
@@ -46,45 +32,6 @@
]
},
{
- "name": "FrameworksServicesTests",
- "file_patterns": ["(/|^)ShortcutService\\.java"],
- "options": [
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest1"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest2"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest3"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest4"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest5"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest6"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest7"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest8"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest9"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest10"
- },
- {
- "include-filter": "com.android.server.pm.ShortcutManagerTest11"
- }
- ]
- },
- {
"name": "CtsShortcutHostTestCases",
"file_patterns": ["(/|^)ShortcutService\\.java"]
},
@@ -130,17 +77,6 @@
]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.content.pm.PackageManagerTests"
- },
- {
- "exclude-annotation": "androidx.test.filters.Suppress"
- }
- ]
- },
- {
"name": "PackageManagerServiceHostTests",
"options": [
{
@@ -188,47 +124,6 @@
},
{
"name": "PackageManagerServiceHostTests"
- },
- {
- "name": "FrameworksServicesTests",
- "options": [
- {
- "install-arg": "-t"
- },
- {
- "include-filter": "com.android.server.pm.UserDataPreparerTest"
- },
- {
- "include-filter": "com.android.server.pm.UserLifecycleStressTest"
- },
- {
- "include-filter": "com.android.server.pm.UserManagerServiceCreateProfileTest"
- },
- {
- "include-filter": "com.android.server.pm.UserManagerServiceIdRecyclingTest"
- },
- {
- "include-filter": "com.android.server.pm.UserManagerServiceTest"
- },
- {
- "include-filter": "com.android.server.pm.UserManagerServiceUserInfoTest"
- },
- {
- "include-filter": "com.android.server.pm.UserManagerServiceUserTypeTest"
- },
- {
- "include-filter": "com.android.server.pm.UserManagerTest"
- },
- {
- "include-filter": "com.android.server.pm.UserRestrictionsUtilsTest"
- },
- {
- "include-filter": "com.android.server.pm.UserSystemPackageInstallerTest"
- },
- {
- "include-filter": "com.android.server.pm.parsing.SystemPartitionParseTest"
- }
- ]
}
],
"imports": [
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 302826f..bc4c8b0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,6 +20,8 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import android.Manifest;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.annotation.ColorRes;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
@@ -44,6 +46,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackagePartitions;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
@@ -85,6 +88,7 @@
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.stats.devicepolicy.DevicePolicyEnums;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -2393,6 +2397,25 @@
return count;
}
+ /**
+ * Returns whether more users of the given type can be added (based on how many users of that
+ * type already exist).
+ */
+ @Override
+ public boolean canAddMoreUsersOfType(String userType) {
+ checkManageOrCreateUsersPermission("check if more users can be added.");
+ final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
+ return userTypeDetails != null && canAddMoreUsersOfType(userTypeDetails);
+ }
+
+ /** Returns whether the creation of users of the given user type is enabled on this device. */
+ @Override
+ public boolean isUserTypeEnabled(String userType) {
+ checkManageOrCreateUsersPermission("check if user type is enabled.");
+ final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
+ return userTypeDetails != null && userTypeDetails.isEnabled();
+ }
+
@Override
public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
return canAddMoreProfilesToUser(UserManager.USER_TYPE_PROFILE_MANAGED, userId,
@@ -3512,6 +3535,39 @@
}
}
+ @Override
+ public UserHandle createUserWithAttributes(
+ String userName, String userType, @UserInfoFlag int flags,
+ Bitmap userIcon,
+ String accountName, String accountType, PersistableBundle accountOptions) {
+ checkManageOrCreateUsersPermission(flags);
+
+ if (someUserHasAccountNoChecks(accountName, accountType)) {
+ throw new ServiceSpecificException(
+ UserManager.USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS);
+ }
+
+ UserInfo userInfo;
+ try {
+ userInfo = createUserInternal(userName, userType, flags,
+ UserHandle.USER_NULL, null);
+
+ if (userInfo == null) {
+ throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN);
+ }
+ } catch (UserManager.CheckedUserOperationException e) {
+ throw e.toServiceSpecificException();
+ }
+
+ if (userIcon != null) {
+ mLocalService.setUserIcon(userInfo.id, userIcon);
+ }
+
+ setSeedAccountDataNoChecks(userInfo.id, accountName, accountType, accountOptions, true);
+
+ return userInfo.getUserHandle();
+ }
+
private UserInfo createUserInternal(@Nullable String name, @NonNull String userType,
@UserInfoFlag int flags, @UserIdInt int parentId,
@Nullable String[] disallowedPackages)
@@ -3680,7 +3736,7 @@
userInfo.creationTime = getCreationTime();
userInfo.partial = true;
userInfo.preCreated = preCreate;
- userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
+ userInfo.lastLoggedInFingerprint = PackagePartitions.FINGERPRINT;
if (userTypeDetails.hasBadge() && parentId != UserHandle.USER_NULL) {
userInfo.profileBadge = getFreeProfileBadgeLU(parentId, userType);
}
@@ -4811,7 +4867,8 @@
t.traceBegin("onBeforeStartUser-" + userId);
final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
- boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
+ boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals(
+ userInfo.lastLoggedInFingerprint);
t.traceBegin("prepareUserData");
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
t.traceEnd();
@@ -4841,7 +4898,8 @@
}
final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
- boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
+ boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals(
+ userInfo.lastLoggedInFingerprint);
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("prepareUserData-" + userId);
@@ -4885,7 +4943,7 @@
if (now > EPOCH_PLUS_30_YEARS) {
userData.info.lastLoggedInTime = now;
}
- userData.info.lastLoggedInFingerprint = Build.FINGERPRINT;
+ userData.info.lastLoggedInFingerprint = PackagePartitions.FINGERPRINT;
scheduleWriteUser(userData);
}
@@ -4934,7 +4992,12 @@
@Override
public void setSeedAccountData(@UserIdInt int userId, String accountName, String accountType,
PersistableBundle accountOptions, boolean persist) {
- checkManageUsersPermission("Require MANAGE_USERS permission to set user seed data");
+ checkManageUsersPermission("set user seed account data");
+ setSeedAccountDataNoChecks(userId, accountName, accountType, accountOptions, persist);
+ }
+
+ private void setSeedAccountDataNoChecks(@UserIdInt int userId, String accountName,
+ String accountType, PersistableBundle accountOptions, boolean persist) {
synchronized (mPackagesLock) {
final UserData userData;
synchronized (mUsersLock) {
@@ -4996,14 +5059,18 @@
}
@Override
- public boolean someUserHasSeedAccount(String accountName, String accountType)
- throws RemoteException {
- checkManageUsersPermission("Cannot check seed account information");
+ public boolean someUserHasSeedAccount(String accountName, String accountType) {
+ checkManageUsersPermission("check seed account information");
+ return someUserHasSeedAccountNoChecks(accountName, accountType);
+ }
+
+ private boolean someUserHasSeedAccountNoChecks(String accountName, String accountType) {
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
final UserData data = mUsers.valueAt(i);
if (data.info.isInitialized()) continue;
+ if (mRemovingUserIds.get(data.info.id)) continue;
if (data.seedAccountName == null || !data.seedAccountName.equals(accountName)) {
continue;
}
@@ -5017,6 +5084,25 @@
}
@Override
+ public boolean someUserHasAccount(String accountName, String accountType) {
+ checkManageOrCreateUsersPermission("check seed account information");
+ return someUserHasAccountNoChecks(accountName, accountType);
+ }
+
+ private boolean someUserHasAccountNoChecks(
+ String accountName, String accountType) {
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
+ return false;
+ }
+
+ final Account account = new Account(accountName, accountType);
+
+ return Binder.withCleanCallingIdentity(() ->
+ AccountManager.get(mContext).someUserHasAccount(account)
+ || someUserHasSeedAccountNoChecks(accountName, accountType));
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 7f42374..328a55f 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -142,7 +142,9 @@
UserManager.DISALLOW_MICROPHONE_TOGGLE,
UserManager.DISALLOW_CAMERA_TOGGLE,
UserManager.DISALLOW_CHANGE_WIFI_STATE,
- UserManager.DISALLOW_WIFI_TETHERING
+ UserManager.DISALLOW_WIFI_TETHERING,
+ UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI
+
});
public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index 4dbc5d5..6d68139 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -399,6 +399,8 @@
verification.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
+ verification.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
+
populateInstallerExtras(verification);
final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 5820489..5371454 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -1043,10 +1043,12 @@
public long deleteOptimizedFiles(ArtPackageInfo packageInfo) {
long freedBytes = 0;
boolean hadErrors = false;
+ final String packageName = packageInfo.getPackageName();
for (String codePath : packageInfo.getCodePaths()) {
for (String isa : packageInfo.getInstructionSets()) {
try {
- freedBytes += mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir());
+ freedBytes += mInstaller.deleteOdex(packageName, codePath, isa,
+ packageInfo.getOatDir());
} catch (InstallerException e) {
Log.e(TAG, "Failed deleting oat files for " + codePath, e);
hadErrors = true;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 0ab1d36..bcb5e72 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -84,8 +84,8 @@
*/
@Nullable
public static PackageInfo generate(AndroidPackage pkg, int[] gids,
- @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId,
+ @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+ long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId,
@Nullable PackageStateInternal pkgSetting) {
return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, userId, null, pkgSetting);
@@ -105,8 +105,8 @@
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
private static PackageInfo generateWithComponents(AndroidPackage pkg, int[] gids,
- @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId,
+ @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+ long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId,
@Nullable ApexInfo apexInfo, @Nullable PackageStateInternal pkgSetting) {
ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId,
pkgSetting);
@@ -209,7 +209,7 @@
*/
@Nullable
public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
- @PackageManager.ApplicationInfoFlags int flags, @NonNull PackageUserState state,
+ @PackageManager.ApplicationInfoFlags long flags, @NonNull PackageUserState state,
int userId, @Nullable PackageStateInternal pkgSetting) {
if (pkg == null) {
return null;
@@ -255,7 +255,7 @@
*/
@Nullable
public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
+ @PackageManager.ComponentInfoFlags long flags, PackageUserState state, int userId,
@Nullable PackageStateInternal pkgSetting) {
return generateActivityInfo(pkg, a, flags, state, null, userId, pkgSetting);
}
@@ -265,7 +265,7 @@
*/
@Nullable
private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId,
@Nullable PackageStateInternal pkgSetting) {
if (a == null) return null;
@@ -291,7 +291,7 @@
*/
@Nullable
public static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
+ @PackageManager.ComponentInfoFlags long flags, PackageUserState state, int userId,
@Nullable PackageStateInternal pkgSetting) {
return generateServiceInfo(pkg, s, flags, state, null, userId, pkgSetting);
}
@@ -301,7 +301,7 @@
*/
@Nullable
private static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId,
@Nullable PackageStateInternal pkgSetting) {
if (s == null) return null;
@@ -326,7 +326,7 @@
*/
@Nullable
public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
@NonNull ApplicationInfo applicationInfo, int userId,
@Nullable PackageStateInternal pkgSetting) {
if (p == null) return null;
@@ -353,7 +353,7 @@
*/
@Nullable
public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
- AndroidPackage pkg, @PackageManager.ComponentInfoFlags int flags, int userId,
+ AndroidPackage pkg, @PackageManager.ComponentInfoFlags long flags, int userId,
@Nullable PackageStateInternal pkgSetting) {
if (i == null) return null;
@@ -381,7 +381,7 @@
// PackageStateInternal os that checkUseInstalledOrHidden filter can apply
@Nullable
public static PermissionInfo generatePermissionInfo(ParsedPermission p,
- @PackageManager.ComponentInfoFlags int flags) {
+ @PackageManager.ComponentInfoFlags long flags) {
// TODO(b/135203078): Remove null checks and make all usages @NonNull
if (p == null) return null;
@@ -391,7 +391,7 @@
@Nullable
public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
- @PackageManager.ComponentInfoFlags int flags) {
+ @PackageManager.ComponentInfoFlags long flags) {
if (pg == null) return null;
// For now, permissions don't have state-adjustable fields; return directly
@@ -400,7 +400,7 @@
@Nullable
public static ArrayMap<String, ProcessInfo> generateProcessInfo(
- Map<String, ParsedProcess> procs, @PackageManager.ComponentInfoFlags int flags) {
+ Map<String, ParsedProcess> procs, @PackageManager.ComponentInfoFlags long flags) {
if (procs == null) {
return null;
}
@@ -423,7 +423,7 @@
*/
public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
PackageStateInternal pkgSetting, PackageUserState state,
- @PackageManager.PackageInfoFlags int flags) {
+ @PackageManager.PackageInfoFlags long flags) {
// Returns false if the package is hidden system app until installed.
if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
&& !state.isInstalled()
@@ -628,7 +628,7 @@
*/
@Nullable
public ApplicationInfo generate(AndroidPackage pkg,
- @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
+ @PackageManager.ApplicationInfoFlags long flags, PackageUserState state, int userId,
@Nullable PackageStateInternal pkgSetting) {
ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
if (appInfo != null) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 61fd5ee..8b2c3a12 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -87,6 +87,17 @@
return paths;
}
+ public static SharedLibraryInfo createSharedLibraryForSdk(AndroidPackage pkg) {
+ return new SharedLibraryInfo(null, pkg.getPackageName(),
+ AndroidPackageUtils.getAllCodePaths(pkg),
+ pkg.getSdkLibName(),
+ pkg.getSdkLibVersionMajor(),
+ SharedLibraryInfo.TYPE_SDK,
+ new VersionedPackage(pkg.getManifestPackageName(),
+ pkg.getLongVersionCode()),
+ null, null, false /* isNative */);
+ }
+
public static SharedLibraryInfo createSharedLibraryForStatic(AndroidPackage pkg) {
return new SharedLibraryInfo(null, pkg.getPackageName(),
AndroidPackageUtils.getAllCodePaths(pkg),
@@ -218,7 +229,8 @@
public static boolean isLibrary(AndroidPackage pkg) {
// TODO(b/135203078): Can parsing just enforce these always match?
- return pkg.getStaticSharedLibName() != null || !pkg.getLibraryNames().isEmpty();
+ return pkg.getSdkLibName() != null || pkg.getStaticSharedLibName() != null
+ || !pkg.getLibraryNames().isEmpty();
}
public static int getHiddenApiEnforcementPolicy(AndroidPackage pkg,
@@ -256,7 +268,7 @@
* Returns false iff the provided flags include the {@link PackageManager#MATCH_SYSTEM_ONLY}
* flag and the provided package is not a system package. Otherwise returns {@code true}.
*/
- public static boolean isMatchForSystemOnly(AndroidPackage pkg, int flags) {
+ public static boolean isMatchForSystemOnly(AndroidPackage pkg, long flags) {
if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
return pkg.isSystem();
}
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 ece0a62b..e207ff1 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -702,6 +702,14 @@
DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, userId),
userId, CONTACTS_PERMISSIONS);
+ // Maps
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ grantPermissionsToSystemPackage(pm,
+ getDefaultSystemHandlerActivityPackageForCategory(pm,
+ Intent.CATEGORY_APP_MAPS, userId),
+ userId, FOREGROUND_LOCATION_PERMISSIONS);
+ }
+
// Email
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackageForCategory(pm,
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 a01c358..4830691 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3484,10 +3484,15 @@
return true;
}
final String permissionName = permission.getName();
- if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) {
+ final ApexManager apexManager = ApexManager.getInstance();
+ final String containingApexPackageName =
+ apexManager.getActiveApexPackageNameContainingPackage(packageName);
+ if (isInSystemConfigPrivAppPermissions(pkg, permissionName,
+ containingApexPackageName)) {
return true;
}
- if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) {
+ if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName,
+ containingApexPackageName)) {
return false;
}
// Updated system apps do not need to be allowlisted
@@ -3504,9 +3509,6 @@
}
// Only enforce the allowlist on boot
if (!mSystemReady) {
- final ApexManager apexManager = ApexManager.getInstance();
- final String containingApexPackageName =
- apexManager.getActiveApexPackageNameContainingPackage(packageName);
final boolean isInUpdatedApex = containingApexPackageName != null
&& !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName,
MATCH_ACTIVE_PACKAGE));
@@ -3530,7 +3532,7 @@
}
private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg,
- @NonNull String permission) {
+ @NonNull String permission, String containingApexPackageName) {
final SystemConfig systemConfig = SystemConfig.getInstance();
final Set<String> permissions;
if (pkg.isVendor()) {
@@ -3539,6 +3541,26 @@
permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName());
} else if (pkg.isSystemExt()) {
permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName());
+ } else if (containingApexPackageName != null) {
+ final Set<String> privAppPermissions = systemConfig.getPrivAppPermissions(
+ pkg.getPackageName());
+ final Set<String> apexPermissions = systemConfig.getApexPrivAppPermissions(
+ containingApexPackageName, pkg.getPackageName());
+ if (privAppPermissions != null) {
+ // TODO(andreionea): Remove check as soon as all apk-in-apex
+ // permission allowlists are migrated.
+ Slog.w(TAG, "Package " + pkg.getPackageName() + " is an APK in APEX,"
+ + " but has permission allowlist on the system image. Please bundle the"
+ + " allowlist in the " + containingApexPackageName + " APEX instead.");
+ if (apexPermissions != null) {
+ permissions = new ArraySet<>(privAppPermissions);
+ permissions.addAll(apexPermissions);
+ } else {
+ permissions = privAppPermissions;
+ }
+ } else {
+ permissions = apexPermissions;
+ }
} else {
permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName());
}
@@ -3546,7 +3568,7 @@
}
private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg,
- @NonNull String permission) {
+ @NonNull String permission, String containingApexPackageName) {
final SystemConfig systemConfig = SystemConfig.getInstance();
final Set<String> permissions;
if (pkg.isVendor()) {
@@ -3555,6 +3577,9 @@
permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName());
} else if (pkg.isSystemExt()) {
permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
+ } else if (containingApexPackageName != null) {
+ permissions = systemConfig.getApexPrivAppDenyPermissions(containingApexPackageName,
+ pkg.getPackageName());
} else {
permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName());
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 82edce6..34575e0 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -27,7 +27,6 @@
import android.content.pm.SigningInfo;
import android.util.SparseArray;
-import com.android.internal.R;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.Settings;
@@ -206,6 +205,18 @@
List<SharedLibraryInfo> getUsesLibraryInfos();
/**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary
+ */
+ @NonNull
+ String[] getUsesSdkLibraries();
+
+ /**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
+ */
+ @NonNull
+ long[] getUsesSdkLibrariesVersionsMajor();
+
+ /**
* @see R.styleable#AndroidManifestUsesStaticLibrary
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 46d32b9..f5e498d 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -130,6 +130,10 @@
@Nullable
private final Integer mSharedUserId;
@NonNull
+ private final String[] mUsesSdkLibraries;
+ @NonNull
+ private final long[] mUsesSdkLibrariesVersionsMajor;
+ @NonNull
private final String[] mUsesStaticLibraries;
@NonNull
private final long[] mUsesStaticLibrariesVersions;
@@ -171,6 +175,8 @@
mPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
mSecondaryCpuAbi = pkgState.getSecondaryCpuAbi();
mSharedUserId = pkgState.getSharedUserId();
+ mUsesSdkLibraries = pkgState.getUsesSdkLibraries();
+ mUsesSdkLibrariesVersionsMajor = pkgState.getUsesSdkLibrariesVersionsMajor();
mUsesStaticLibraries = pkgState.getUsesStaticLibraries();
mUsesStaticLibrariesVersions = pkgState.getUsesStaticLibrariesVersions();
mUsesLibraryInfos = pkgState.getUsesLibraryInfos();
@@ -262,6 +268,11 @@
return getBoolean(Booleans.VENDOR);
}
+ @Override
+ public long getVersionCode() {
+ return mLongVersionCode;
+ }
+
/**
* @hide
*/
@@ -500,10 +511,10 @@
}
@DataClass.Generated(
- time = 1633375703010L,
+ time = 1637977288540L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\npublic static android.content.pm.pkg.PackageUserState copy(android.content.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [android.content.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
@@ -579,7 +590,7 @@
}
@DataClass.Generated.Member
- public long getVersionCode() {
+ public long getLongVersionCode() {
return mLongVersionCode;
}
@@ -609,6 +620,16 @@
}
@DataClass.Generated.Member
+ public @NonNull String[] getUsesSdkLibraries() {
+ return mUsesSdkLibraries;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull long[] getUsesSdkLibrariesVersionsMajor() {
+ return mUsesSdkLibrariesVersionsMajor;
+ }
+
+ @DataClass.Generated.Member
public @NonNull String[] getUsesStaticLibraries() {
return mUsesStaticLibraries;
}
@@ -650,10 +671,10 @@
}
@DataClass.Generated(
- time = 1633375703038L,
+ time = 1637977288579L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mFirstInstallTime\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<android.content.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.PackageSetting)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mFirstInstallTime\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
index 6744ff5..09b9d31 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
@@ -27,7 +27,7 @@
public class PackageStateUtils {
- public static boolean isMatch(PackageState packageState, int flags) {
+ public static boolean isMatch(PackageState packageState, long flags) {
if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
return packageState.isSystem();
}
@@ -54,7 +54,7 @@
}
public static boolean isEnabledAndMatches(@Nullable PackageStateInternal packageState,
- ComponentInfo componentInfo, int flags, int userId) {
+ ComponentInfo componentInfo, long flags, int userId) {
if (packageState == null) return false;
final PackageUserState userState = packageState.getUserStateOrDefault(userId);
@@ -62,7 +62,7 @@
}
public static boolean isEnabledAndMatches(@Nullable PackageStateInternal packageState,
- @NonNull ParsedMainComponent component, int flags, int userId) {
+ @NonNull ParsedMainComponent component, long flags, int userId) {
if (packageState == null) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 1f024ea..471f38a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -391,7 +391,7 @@
*/
@ApprovalLevel
int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
- @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId);
+ @PackageManager.ResolveInfoFlags long resolveInfoFlags, @UserIdInt int userId);
/**
* @return the domain verification set ID for the given package, or null if the ID is
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 0fb8475..661e67d 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -1721,7 +1721,7 @@
@Override
public int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting,
- @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags,
+ @NonNull Intent intent, @PackageManager.ResolveInfoFlags long resolveInfoFlags,
@UserIdInt int userId) {
String packageName = pkgSetting.getPackageName();
if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 246810f..12cce0d 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -49,7 +49,7 @@
}
public static boolean isDomainVerificationIntent(Intent intent,
- @PackageManager.ResolveInfoFlags int resolveInfoFlags) {
+ @PackageManager.ResolveInfoFlags long resolveInfoFlags) {
if (!intent.isWebIntent()) {
return false;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2369c5e..5c15a84 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -280,6 +280,7 @@
static final int MULTI_PRESS_POWER_NOTHING = 0;
static final int MULTI_PRESS_POWER_THEATER_MODE = 1;
static final int MULTI_PRESS_POWER_BRIGHTNESS_BOOST = 2;
+ static final int MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY = 3;
// must match: config_longPressOnBackBehavior in config.xml
static final int LONG_PRESS_BACK_NOTHING = 0;
@@ -332,6 +333,10 @@
private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
/**
* Keyguard stuff
@@ -493,6 +498,7 @@
long mLongPressOnPowerAssistantTimeoutMs;
int mVeryLongPressOnPowerBehavior;
int mDoublePressOnPowerBehavior;
+ ComponentName mPowerDoublePressTargetActivity;
int mTriplePressOnPowerBehavior;
int mLongPressOnBackBehavior;
int mShortPressOnSleepBehavior;
@@ -517,7 +523,6 @@
private boolean mPendingKeyguardOccluded;
private boolean mKeyguardOccludedChanged;
- private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
@@ -949,6 +954,8 @@
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == 3) {
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 && !beganFromNonInteractive) {
if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {
Slog.i(TAG, "Suppressing power key because the user is interacting with the "
@@ -1081,6 +1088,30 @@
}
mPowerManager.boostScreenBrightness(eventTime);
break;
+ case MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY:
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "Executing the double press power action.");
+ }
+ final boolean keyguardActive =
+ mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
+ if (!keyguardActive) {
+ Intent intent = new Intent();
+ if (mPowerDoublePressTargetActivity != null) {
+ intent.setComponent(mPowerDoublePressTargetActivity);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
+ intent, /* flags= */0);
+ if (resolveInfo != null) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ } else {
+ Slog.e(TAG, "Could not resolve activity with : "
+ + mPowerDoublePressTargetActivity.flattenToString()
+ + " name.");
+ }
+ }
+ }
+ break;
}
}
@@ -1095,7 +1126,13 @@
// GestureLauncherService.
// To speed up the handling of single-press of power button inside SingleKeyGestureDetector,
// however, we limit the max count to the number of button presses actually handled by the
- // SingleKeyGestureDetector.
+ // SingleKeyGestureDetector except for wearable devices, where we want to de-dup the double
+ // press gesture with the emergency gesture.
+ if (mHasFeatureWatch
+ && GestureLauncherService.isEmergencyGestureSettingEnabled(
+ mContext, ActivityManager.getCurrentUser())) {
+ return 5;
+ }
if (mTriplePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) {
return 3;
}
@@ -1115,21 +1152,21 @@
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Global Actions");
showGlobalActions();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Shut Off");
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
break;
case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Go To Voice Assist");
// Some devices allow the voice assistant intent during setup (and use that intent
// to launch something else, like Settings). So we explicitly allow that via the
@@ -1153,7 +1190,7 @@
break;
case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Very Long Press - Show Global Actions");
showGlobalActions();
break;
@@ -1815,9 +1852,6 @@
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
mLogger = new MetricsLogger();
- mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
- .createSleepTokenAcquirer("ScreenOff");
-
Resources res = mContext.getResources();
mWakeOnDpadKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
@@ -1933,6 +1967,9 @@
com.android.internal.R.integer.config_veryLongPressOnPowerBehavior);
mDoublePressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_doublePressOnPowerBehavior);
+ mPowerDoublePressTargetActivity = ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_doublePressOnPowerTargetActivity));
mTriplePressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_triplePressOnPowerBehavior);
mShortPressOnSleepBehavior = mContext.getResources().getInteger(
@@ -2098,7 +2135,8 @@
mPowerKeyHandled = true;
break;
case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(
+ HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power + Volume Up - Global Actions");
showGlobalActions();
mPowerKeyHandled = true;
@@ -4483,7 +4521,6 @@
if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
if (displayId == DEFAULT_DISPLAY) {
- updateScreenOffSleepToken(true);
mRequestedOrSleepingDefaultDisplay = false;
mDefaultDisplayPolicy.screenTurnedOff();
synchronized (mLock) {
@@ -4516,7 +4553,6 @@
if (displayId == DEFAULT_DISPLAY) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
0 /* cookie */);
- updateScreenOffSleepToken(false);
mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
mBootAnimationDismissable = false;
@@ -5037,15 +5073,6 @@
}
}
- // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
- private void updateScreenOffSleepToken(boolean acquire) {
- if (acquire) {
- mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY);
- } else {
- mScreenOffSleepTokenAcquirer.release(DEFAULT_DISPLAY);
- }
- }
-
/** {@inheritDoc} */
@Override
public void enableScreenAfterBoot() {
@@ -5291,7 +5318,7 @@
return false;
}
- mVibrator.vibrate(uid, packageName, effect, reason, TOUCH_VIBRATION_ATTRIBUTES);
+ mVibrator.vibrate(uid, packageName, effect, reason, getVibrationAttributes(effectId));
return true;
}
@@ -5320,6 +5347,7 @@
case HapticFeedbackConstants.GESTURE_START:
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.LONG_PRESS:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
case HapticFeedbackConstants.EDGE_SQUEEZE:
return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
case HapticFeedbackConstants.REJECT:
@@ -5358,6 +5386,22 @@
}
}
+ private VibrationAttributes getVibrationAttributes(int effectId) {
+ switch (effectId) {
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
+ case HapticFeedbackConstants.ASSISTANT_BUTTON:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+ case HapticFeedbackConstants.ROTARY_SCROLL_TICK:
+ case HapticFeedbackConstants.ROTARY_SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.ROTARY_SCROLL_LIMIT:
+ return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
+ default:
+ return TOUCH_VIBRATION_ATTRIBUTES;
+ }
+ }
+
@Override
public void keepScreenOnStartedLw() {
}
@@ -5679,6 +5723,8 @@
return "MULTI_PRESS_POWER_THEATER_MODE";
case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
return "MULTI_PRESS_POWER_BRIGHTNESS_BOOST";
+ case MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY:
+ return "MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY";
default:
return Integer.toString(behavior);
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 5ec2f83..4571cf3 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -27,7 +27,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -190,14 +189,6 @@
String getOwningPackage();
/**
- * Retrieve the current LayoutParams of the window.
- *
- * @return WindowManager.LayoutParams The window's internal LayoutParams
- * instance.
- */
- public WindowManager.LayoutParams getAttrs();
-
- /**
* Retrieve the type of the top-level window.
*
* @return the base type of the parent window if attached or its own type otherwise
@@ -658,26 +649,6 @@
public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs);
/**
- * @return whether {@param win} can be hidden by Keyguard
- */
- default boolean canBeHiddenByKeyguardLw(WindowState win) {
- // Keyguard visibility of window from activities are determined over activity visibility.
- if (win.getBaseType() == TYPE_BASE_APPLICATION) {
- return false;
- }
- switch (win.getAttrs().type) {
- case TYPE_NOTIFICATION_SHADE:
- case TYPE_STATUS_BAR:
- case TYPE_NAVIGATION_BAR:
- case TYPE_WALLPAPER:
- return false;
- default:
- // Hide only windows below the keyguard host window.
- return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
- }
- }
-
- /**
* Create and return an animation to re-display a window that was force hidden by Keyguard.
*/
public Animation createHiddenByKeyguardExit(boolean onWallpaper,
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 6d0f08d..70a804b 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -110,8 +110,8 @@
private static final VibrationEffect CHARGING_VIBRATION_EFFECT =
VibrationEffect.createWaveform(CHARGING_VIBRATION_TIME, CHARGING_VIBRATION_AMPLITUDE,
-1);
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
private final Object mLock = new Object();
@@ -807,7 +807,7 @@
final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
if (vibrate) {
- mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, TOUCH_VIBRATION_ATTRIBUTES);
+ mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
}
// play sound
diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
new file mode 100644
index 0000000..f519ced
--- /dev/null
+++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
@@ -0,0 +1,91 @@
+/*
+ * 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.server.security;
+
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelDuration;
+import android.os.RemoteException;
+import android.security.attestationverification.AttestationProfile;
+import android.security.attestationverification.IAttestationVerificationManagerService;
+import android.security.attestationverification.IVerificationResult;
+import android.security.attestationverification.VerificationToken;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.server.SystemService;
+
+/**
+ * A {@link SystemService} which provides functionality related to verifying attestations of
+ * (usually) remote computing environments.
+ *
+ * @hide
+ */
+public class AttestationVerificationManagerService extends SystemService {
+
+ private static final String TAG = "AVF";
+
+ public AttestationVerificationManagerService(final Context context) {
+ super(context);
+ }
+
+ private final IBinder mService = new IAttestationVerificationManagerService.Stub() {
+ @Override
+ public void verifyAttestation(
+ AttestationProfile profile,
+ int localBindingType,
+ Bundle requirements,
+ byte[] attestation,
+ AndroidFuture resultCallback) throws RemoteException {
+ try {
+ Slog.d(TAG, "verifyAttestation");
+ verifyAttestationForAllVerifiers(profile, localBindingType, requirements,
+ attestation, resultCallback);
+ } catch (Throwable t) {
+ Slog.e(TAG, "failed to verify attestation", t);
+ throw ExceptionUtils.propagate(t, RemoteException.class);
+ }
+ }
+
+ @Override
+ public void verifyToken(VerificationToken token, ParcelDuration parcelDuration,
+ AndroidFuture resultCallback) throws RemoteException {
+ // TODO(b/201696614): Implement
+ resultCallback.complete(RESULT_UNKNOWN);
+ }
+ };
+
+ private void verifyAttestationForAllVerifiers(
+ AttestationProfile profile, int localBindingType, Bundle requirements,
+ byte[] attestation, AndroidFuture<IVerificationResult> resultCallback) {
+ // TODO(b/201696614): Implement
+ IVerificationResult result = new IVerificationResult();
+ result.resultCode = RESULT_UNKNOWN;
+ result.token = null;
+ resultCallback.complete(result);
+ }
+
+ @Override
+ public void onStart() {
+ Slog.d(TAG, "Started");
+ publishBinderService(Context.ATTESTATION_VERIFICATION_SERVICE, mService);
+ }
+}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 7cdae58..eb69ff7 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -56,6 +56,7 @@
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO;
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__MANUAL;
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__TELEPHONY;
+import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
@@ -63,6 +64,8 @@
import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+import static libcore.io.IoUtils.closeQuietly;
+
import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
@@ -221,6 +224,7 @@
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -3407,14 +3411,17 @@
pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
metricsState.isTelephonyDetectionSupported(),
metricsState.isGeoDetectionSupported(),
- metricsState.isUserLocationEnabled(),
+ metricsState.getUserLocationEnabledSetting(),
metricsState.getAutoDetectionEnabledSetting(),
metricsState.getGeoDetectionEnabledSetting(),
convertToMetricsDetectionMode(metricsState.getDetectionMode()),
metricsState.getDeviceTimeZoneIdOrdinal(),
- metricsState.getLatestManualSuggestionProtoBytes(),
- metricsState.getLatestTelephonySuggestionProtoBytes(),
- metricsState.getLatestGeolocationSuggestionProtoBytes()
+ convertTimeZoneSuggestionToProtoBytes(
+ metricsState.getLatestManualSuggestion()),
+ convertTimeZoneSuggestionToProtoBytes(
+ metricsState.getLatestTelephonySuggestion()),
+ convertTimeZoneSuggestionToProtoBytes(
+ metricsState.getLatestGeolocationSuggestion())
));
} catch (RuntimeException e) {
Slog.e(TAG, "Getting time zone detection state failed: ", e);
@@ -3425,7 +3432,8 @@
return StatsManager.PULL_SUCCESS;
}
- private int convertToMetricsDetectionMode(int detectionMode) {
+ private static int convertToMetricsDetectionMode(
+ @MetricsTimeZoneDetectorState.DetectionMode int detectionMode) {
switch (detectionMode) {
case MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL:
return TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__MANUAL;
@@ -3434,10 +3442,38 @@
case MetricsTimeZoneDetectorState.DETECTION_MODE_TELEPHONY:
return TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__TELEPHONY;
default:
- throw new IllegalArgumentException("" + detectionMode);
+ return TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN;
}
}
+ @Nullable
+ private static byte[] convertTimeZoneSuggestionToProtoBytes(
+ @Nullable MetricsTimeZoneDetectorState.MetricsTimeZoneSuggestion suggestion) {
+ if (suggestion == null) {
+ return null;
+ }
+
+ // We don't get access to the atoms.proto definition for nested proto fields, so we use
+ // an identically specified proto.
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream(byteArrayOutputStream);
+ int typeProtoValue = suggestion.isCertain()
+ ? android.app.time.MetricsTimeZoneSuggestion.CERTAIN
+ : android.app.time.MetricsTimeZoneSuggestion.UNCERTAIN;
+ protoOutputStream.write(android.app.time.MetricsTimeZoneSuggestion.TYPE,
+ typeProtoValue);
+ if (suggestion.isCertain()) {
+ for (int zoneIdOrdinal : suggestion.getZoneIdOrdinals()) {
+ protoOutputStream.write(
+ android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
+ zoneIdOrdinal);
+ }
+ }
+ protoOutputStream.flush();
+ closeQuietly(byteArrayOutputStream);
+ return byteArrayOutputStream.toByteArray();
+ }
+
private void registerExternalStorageInfo() {
int tagId = FrameworkStatsLog.EXTERNAL_STORAGE_INFO;
mStatsManager.setPullAtomCallback(
diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index b00540f..2923148 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -24,7 +24,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.FuseUnavailableMountException;
import com.android.internal.util.Preconditions;
-import com.android.server.NativeDaemonConnectorException;
+import com.android.server.AppFuseMountException;
import libcore.io.IoUtils;
import java.util.concurrent.CountDownLatch;
@@ -55,7 +55,7 @@
}
public ParcelFileDescriptor addBridge(MountScope mountScope)
- throws FuseUnavailableMountException, NativeDaemonConnectorException {
+ throws FuseUnavailableMountException, AppFuseMountException {
/*
** Dead Lock between Java lock (AppFuseBridge.java) and Native lock (FuseBridgeLoop.cc)
**
@@ -112,7 +112,7 @@
try {
int flags = FileUtils.translateModePfdToPosix(mode);
return scope.openFile(mountId, fileId, flags);
- } catch (NativeDaemonConnectorException error) {
+ } catch (AppFuseMountException error) {
throw new FuseUnavailableMountException(mountId);
}
}
@@ -160,9 +160,9 @@
return mMountResult;
}
- public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException;
+ public abstract ParcelFileDescriptor open() throws AppFuseMountException;
public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
- throws NativeDaemonConnectorException;
+ throws AppFuseMountException;
}
private native long native_new();
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index 477ebf6..d24a3df 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -65,6 +65,7 @@
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE,
KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
+ KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
})
@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@Retention(RetentionPolicy.SOURCE)
@@ -139,6 +140,14 @@
"location_time_zone_detection_setting_enabled_default";
/**
+ * The key to control support for time zone detection falling back to telephony detection under
+ * certain circumstances.
+ */
+ public static final @DeviceConfigKey String
+ KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED =
+ "time_zone_detector_telephony_fallback_supported";
+
+ /**
* The key to override the time detector origin priorities configuration. A comma-separated list
* of strings that will be passed to {@link TimeDetectorStrategy#stringToOrigin(String)}.
* All values must be recognized or the override value will be ignored.
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 22814b3..65f077e 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -39,21 +39,23 @@
private final boolean mTelephonyDetectionSupported;
private final boolean mGeoDetectionSupported;
- private final boolean mAutoDetectionEnabled;
+ private final boolean mTelephonyFallbackSupported;
+ private final boolean mAutoDetectionEnabledSetting;
private final @UserIdInt int mUserId;
private final boolean mUserConfigAllowed;
- private final boolean mLocationEnabled;
- private final boolean mGeoDetectionEnabled;
+ private final boolean mLocationEnabledSetting;
+ private final boolean mGeoDetectionEnabledSetting;
private ConfigurationInternal(Builder builder) {
mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported;
mGeoDetectionSupported = builder.mGeoDetectionSupported;
- mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
+ mTelephonyFallbackSupported = builder.mTelephonyFallbackSupported;
+ mAutoDetectionEnabledSetting = builder.mAutoDetectionEnabledSetting;
mUserId = builder.mUserId;
mUserConfigAllowed = builder.mUserConfigAllowed;
- mLocationEnabled = builder.mLocationEnabled;
- mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
+ mLocationEnabledSetting = builder.mLocationEnabledSetting;
+ mGeoDetectionEnabledSetting = builder.mGeoDetectionEnabledSetting;
}
/** Returns true if the device supports any form of auto time zone detection. */
@@ -71,9 +73,17 @@
return mGeoDetectionSupported;
}
+ /**
+ * Returns true if the device supports time zone detection falling back to telephony detection
+ * under certain circumstances.
+ */
+ public boolean isTelephonyFallbackSupported() {
+ return mTelephonyFallbackSupported;
+ }
+
/** Returns the value of the auto time zone detection enabled setting. */
public boolean getAutoDetectionEnabledSetting() {
- return mAutoDetectionEnabled;
+ return mAutoDetectionEnabledSetting;
}
/**
@@ -81,7 +91,7 @@
* from the raw setting value.
*/
public boolean getAutoDetectionEnabledBehavior() {
- return isAutoDetectionSupported() && mAutoDetectionEnabled;
+ return isAutoDetectionSupported() && mAutoDetectionEnabledSetting;
}
/** Returns the ID of the user this configuration is associated with. */
@@ -101,13 +111,13 @@
}
/** Returns true if user's location can be used generally. */
- public boolean isLocationEnabled() {
- return mLocationEnabled;
+ public boolean getLocationEnabledSetting() {
+ return mLocationEnabledSetting;
}
/** Returns the value of the geolocation time zone detection enabled setting. */
public boolean getGeoDetectionEnabledSetting() {
- return mGeoDetectionEnabled;
+ return mGeoDetectionEnabledSetting;
}
/**
@@ -117,7 +127,7 @@
public boolean getGeoDetectionEnabledBehavior() {
return getAutoDetectionEnabledBehavior()
&& isGeoDetectionSupported()
- && isLocationEnabled()
+ && getLocationEnabledSetting()
&& getGeoDetectionEnabledSetting();
}
@@ -154,7 +164,7 @@
final int configureGeolocationDetectionEnabledCapability;
if (!deviceHasLocationTimeZoneDetection) {
configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
- } else if (!mAutoDetectionEnabled || !isLocationEnabled()) {
+ } else if (!mAutoDetectionEnabledSetting || !getLocationEnabledSetting()) {
configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_APPLICABLE;
} else {
configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED;
@@ -195,10 +205,10 @@
public ConfigurationInternal merge(TimeZoneConfiguration newConfiguration) {
Builder builder = new Builder(this);
if (newConfiguration.hasIsAutoDetectionEnabled()) {
- builder.setAutoDetectionEnabled(newConfiguration.isAutoDetectionEnabled());
+ builder.setAutoDetectionEnabledSetting(newConfiguration.isAutoDetectionEnabled());
}
if (newConfiguration.hasIsGeoDetectionEnabled()) {
- builder.setGeoDetectionEnabled(newConfiguration.isGeoDetectionEnabled());
+ builder.setGeoDetectionEnabledSetting(newConfiguration.isGeoDetectionEnabled());
}
return builder.build();
}
@@ -216,16 +226,17 @@
&& mUserConfigAllowed == that.mUserConfigAllowed
&& mTelephonyDetectionSupported == that.mTelephonyDetectionSupported
&& mGeoDetectionSupported == that.mGeoDetectionSupported
- && mAutoDetectionEnabled == that.mAutoDetectionEnabled
- && mLocationEnabled == that.mLocationEnabled
- && mGeoDetectionEnabled == that.mGeoDetectionEnabled;
+ && mTelephonyFallbackSupported == that.mTelephonyFallbackSupported
+ && mAutoDetectionEnabledSetting == that.mAutoDetectionEnabledSetting
+ && mLocationEnabledSetting == that.mLocationEnabledSetting
+ && mGeoDetectionEnabledSetting == that.mGeoDetectionEnabledSetting;
}
@Override
public int hashCode() {
return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported,
- mGeoDetectionSupported, mAutoDetectionEnabled, mLocationEnabled,
- mGeoDetectionEnabled);
+ mGeoDetectionSupported, mTelephonyFallbackSupported, mAutoDetectionEnabledSetting,
+ mLocationEnabledSetting, mGeoDetectionEnabledSetting);
}
@Override
@@ -235,9 +246,10 @@
+ ", mUserConfigAllowed=" + mUserConfigAllowed
+ ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported
+ ", mGeoDetectionSupported=" + mGeoDetectionSupported
- + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled
- + ", mLocationEnabled=" + mLocationEnabled
- + ", mGeoDetectionEnabled=" + mGeoDetectionEnabled
+ + ", mTelephonyFallbackSupported=" + mTelephonyFallbackSupported
+ + ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting
+ + ", mLocationEnabledSetting=" + mLocationEnabledSetting
+ + ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting
+ '}';
}
@@ -251,9 +263,10 @@
private boolean mUserConfigAllowed;
private boolean mTelephonyDetectionSupported;
private boolean mGeoDetectionSupported;
- private boolean mAutoDetectionEnabled;
- private boolean mLocationEnabled;
- private boolean mGeoDetectionEnabled;
+ private boolean mTelephonyFallbackSupported;
+ private boolean mAutoDetectionEnabledSetting;
+ private boolean mLocationEnabledSetting;
+ private boolean mGeoDetectionEnabledSetting;
/**
* Creates a new Builder with only the userId set.
@@ -269,10 +282,11 @@
this.mUserId = toCopy.mUserId;
this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported;
+ this.mTelephonyFallbackSupported = toCopy.mTelephonyFallbackSupported;
this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
- this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled;
- this.mLocationEnabled = toCopy.mLocationEnabled;
- this.mGeoDetectionEnabled = toCopy.mGeoDetectionEnabled;
+ this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting;
+ this.mLocationEnabledSetting = toCopy.mLocationEnabledSetting;
+ this.mGeoDetectionEnabledSetting = toCopy.mGeoDetectionEnabledSetting;
}
/**
@@ -300,26 +314,35 @@
}
/**
+ * Sets whether time zone detection supports falling back to telephony detection under
+ * certain circumstances.
+ */
+ public Builder setTelephonyFallbackSupported(boolean supported) {
+ mTelephonyFallbackSupported = supported;
+ return this;
+ }
+
+ /**
* Sets the value of the automatic time zone detection enabled setting for this device.
*/
- public Builder setAutoDetectionEnabled(boolean enabled) {
- mAutoDetectionEnabled = enabled;
+ public Builder setAutoDetectionEnabledSetting(boolean enabled) {
+ mAutoDetectionEnabledSetting = enabled;
return this;
}
/**
* Sets the value of the location mode setting for this user.
*/
- public Builder setLocationEnabled(boolean enabled) {
- mLocationEnabled = enabled;
+ public Builder setLocationEnabledSetting(boolean enabled) {
+ mLocationEnabledSetting = enabled;
return this;
}
/**
* Sets the value of the geolocation time zone detection setting for this user.
*/
- public Builder setGeoDetectionEnabled(boolean enabled) {
- mGeoDetectionEnabled = enabled;
+ public Builder setGeoDetectionEnabledSetting(boolean enabled) {
+ mGeoDetectionEnabledSetting = enabled;
return this;
}
diff --git a/services/core/java/com/android/server/timezonedetector/DeviceActivityMonitor.java b/services/core/java/com/android/server/timezonedetector/DeviceActivityMonitor.java
new file mode 100644
index 0000000..62092ec
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/DeviceActivityMonitor.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.timezonedetector;
+
+import android.annotation.NonNull;
+
+/**
+ * The interface for the class that is responsible for detecting device activities relevant to
+ * time zone detection. This interface exists to decouple parts of the time zone detector from each
+ * other and to enable easier testing.
+ *
+ * @hide
+ */
+interface DeviceActivityMonitor extends Dumpable {
+
+ /** Adds a listener. */
+ void addListener(@NonNull Listener listener);
+
+ /**
+ * A listener for device activities. See {@link DeviceActivityMonitor#addListener(Listener)}.
+ */
+ interface Listener {
+ /** A flight has completed. */
+ void onFlightComplete();
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/DeviceActivityMonitorImpl.java b/services/core/java/com/android/server/timezonedetector/DeviceActivityMonitorImpl.java
new file mode 100644
index 0000000..8c9bd3b
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/DeviceActivityMonitorImpl.java
@@ -0,0 +1,94 @@
+/*
+ * 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.server.timezonedetector;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The real implementation of {@link DeviceActivityMonitor}.
+ */
+class DeviceActivityMonitorImpl implements DeviceActivityMonitor {
+
+ private static final String LOG_TAG = TimeZoneDetectorService.TAG;
+ private static final boolean DBG = TimeZoneDetectorService.DBG;
+
+ static DeviceActivityMonitor create(@NonNull Context context, @NonNull Handler handler) {
+ return new DeviceActivityMonitorImpl(context, handler);
+ }
+
+ @GuardedBy("this")
+ @NonNull
+ private final List<Listener> mListeners = new ArrayList<>();
+
+ private DeviceActivityMonitorImpl(@NonNull Context context, @NonNull Handler handler) {
+ // The way this "detects" a flight concluding is by the user explicitly turning off airplane
+ // mode. Smarter heuristics would be nice.
+ ContentResolver contentResolver = context.getContentResolver();
+ ContentObserver airplaneModeObserver = new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean unused) {
+ try {
+ int state = Settings.Global.getInt(
+ contentResolver, Settings.Global.AIRPLANE_MODE_ON);
+ if (state == 0) {
+ notifyFlightComplete();
+ }
+ } catch (Settings.SettingNotFoundException e) {
+ Slog.e(LOG_TAG, "Unable to read airplane mode state", e);
+ }
+ }
+ };
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+ true /* notifyForDescendants */,
+ airplaneModeObserver);
+ }
+
+ @Override
+ public synchronized void addListener(Listener listener) {
+ Objects.requireNonNull(listener);
+ mListeners.add(listener);
+ }
+
+ private synchronized void notifyFlightComplete() {
+ if (DBG) {
+ Slog.d(LOG_TAG, "notifyFlightComplete");
+ }
+
+ for (Listener listener : mListeners) {
+ listener.onFlightComplete();
+ }
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ // No-op right now: no state to dump.
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index ec620b5..0ec8826c 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -16,11 +16,13 @@
package com.android.server.timezonedetector;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
+import android.os.SystemClock;
import android.os.SystemProperties;
import java.util.Objects;
@@ -79,4 +81,9 @@
AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
alarmManager.setTimeZone(zoneId);
}
+
+ @Override
+ public @ElapsedRealtimeLong long elapsedRealtimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
}
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
index 9eb6a45..f156f8c 100644
--- a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -16,16 +16,12 @@
package com.android.server.timezonedetector;
-import static libcore.io.IoUtils.closeQuietly;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
-import android.util.proto.ProtoOutputStream;
-import java.io.ByteArrayOutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -50,22 +46,17 @@
value = { DETECTION_MODE_MANUAL, DETECTION_MODE_GEO, DETECTION_MODE_TELEPHONY})
@Retention(RetentionPolicy.SOURCE)
@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
- @interface DetectionMode {};
+ public @interface DetectionMode {};
public static final @DetectionMode int DETECTION_MODE_MANUAL = 0;
public static final @DetectionMode int DETECTION_MODE_GEO = 1;
public static final @DetectionMode int DETECTION_MODE_TELEPHONY = 2;
- @NonNull
- private final ConfigurationInternal mConfigurationInternal;
- @NonNull
+ @NonNull private final ConfigurationInternal mConfigurationInternal;
private final int mDeviceTimeZoneIdOrdinal;
- @Nullable
- private final MetricsTimeZoneSuggestion mLatestManualSuggestion;
- @Nullable
- private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion;
- @Nullable
- private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion;
+ @Nullable private final MetricsTimeZoneSuggestion mLatestManualSuggestion;
+ @Nullable private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion;
+ @Nullable private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion;
private MetricsTimeZoneDetectorState(
@NonNull ConfigurationInternal configurationInternal,
@@ -94,16 +85,16 @@
int deviceTimeZoneIdOrdinal =
tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId));
- MetricsTimeZoneSuggestion latestObfuscatedManualSuggestion =
+ MetricsTimeZoneSuggestion latestCanonicalManualSuggestion =
createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion);
- MetricsTimeZoneSuggestion latestObfuscatedTelephonySuggestion =
+ MetricsTimeZoneSuggestion latestCanonicalTelephonySuggestion =
createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion);
- MetricsTimeZoneSuggestion latestObfuscatedGeolocationSuggestion =
+ MetricsTimeZoneSuggestion latestCanonicalGeolocationSuggestion =
createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion);
return new MetricsTimeZoneDetectorState(
- configurationInternal, deviceTimeZoneIdOrdinal, latestObfuscatedManualSuggestion,
- latestObfuscatedTelephonySuggestion, latestObfuscatedGeolocationSuggestion);
+ configurationInternal, deviceTimeZoneIdOrdinal, latestCanonicalManualSuggestion,
+ latestCanonicalTelephonySuggestion, latestCanonicalGeolocationSuggestion);
}
/** Returns true if the device supports telephony time zone detection. */
@@ -116,9 +107,14 @@
return mConfigurationInternal.isGeoDetectionSupported();
}
+ /** Returns true if the device supports telephony time zone detection fallback. */
+ public boolean isTelephonyTimeZoneFallbackSupported() {
+ return mConfigurationInternal.isTelephonyFallbackSupported();
+ }
+
/** Returns true if user's location can be used generally. */
- public boolean isUserLocationEnabled() {
- return mConfigurationInternal.isLocationEnabled();
+ public boolean getUserLocationEnabledSetting() {
+ return mConfigurationInternal.getLocationEnabledSetting();
}
/** Returns the value of the geolocation time zone detection enabled setting. */
@@ -149,36 +145,32 @@
* Returns the ordinal for the device's currently set time zone ID.
* See {@link MetricsTimeZoneDetectorState} for information about ordinals.
*/
- @NonNull
public int getDeviceTimeZoneIdOrdinal() {
return mDeviceTimeZoneIdOrdinal;
}
/**
- * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last manual
- * suggestion received.
+ * Returns a canonical form of the last manual suggestion received.
*/
@Nullable
- public byte[] getLatestManualSuggestionProtoBytes() {
- return suggestionProtoBytes(mLatestManualSuggestion);
+ public MetricsTimeZoneSuggestion getLatestManualSuggestion() {
+ return mLatestManualSuggestion;
}
/**
- * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last, best
- * telephony suggestion received.
+ * Returns a canonical form of the last telephony suggestion received.
*/
@Nullable
- public byte[] getLatestTelephonySuggestionProtoBytes() {
- return suggestionProtoBytes(mLatestTelephonySuggestion);
+ public MetricsTimeZoneSuggestion getLatestTelephonySuggestion() {
+ return mLatestTelephonySuggestion;
}
/**
- * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last geolocation
- * suggestion received.
+ * Returns a canonical form of last geolocation suggestion received.
*/
@Nullable
- public byte[] getLatestGeolocationSuggestionProtoBytes() {
- return suggestionProtoBytes(mLatestGeolocationSuggestion);
+ public MetricsTimeZoneSuggestion getLatestGeolocationSuggestion() {
+ return mLatestGeolocationSuggestion;
}
@Override
@@ -214,14 +206,6 @@
+ '}';
}
- private static byte[] suggestionProtoBytes(
- @Nullable MetricsTimeZoneSuggestion suggestion) {
- if (suggestion == null) {
- return null;
- }
- return suggestion.toBytes();
- }
-
@Nullable
private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
@NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
@@ -265,10 +249,11 @@
}
/**
- * A Java class that closely matches the android.app.time.MetricsTimeZoneSuggestion
- * proto definition.
+ * A Java class that represents a generic time zone suggestion, i.e. one that is independent of
+ * origin-specific information. This closely matches the metrics atoms.proto
+ * MetricsTimeZoneSuggestion proto definition.
*/
- private static final class MetricsTimeZoneSuggestion {
+ public static final class MetricsTimeZoneSuggestion {
@Nullable
private final int[] mZoneIdOrdinals;
@@ -281,42 +266,21 @@
return new MetricsTimeZoneSuggestion(null);
}
- public static MetricsTimeZoneSuggestion createCertain(
+ @NonNull
+ static MetricsTimeZoneSuggestion createCertain(
@NonNull int[] zoneIdOrdinals) {
return new MetricsTimeZoneSuggestion(zoneIdOrdinals);
}
- boolean isCertain() {
+ public boolean isCertain() {
return mZoneIdOrdinals != null;
}
@Nullable
- int[] getZoneIdOrdinals() {
+ public int[] getZoneIdOrdinals() {
return mZoneIdOrdinals;
}
- byte[] toBytes() {
- // We don't get access to the atoms.proto definition for nested proto fields, so we use
- // an identically specified proto.
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- ProtoOutputStream protoOutputStream = new ProtoOutputStream(byteArrayOutputStream);
- int typeProtoValue = isCertain()
- ? android.app.time.MetricsTimeZoneSuggestion.CERTAIN
- : android.app.time.MetricsTimeZoneSuggestion.UNCERTAIN;
- protoOutputStream.write(android.app.time.MetricsTimeZoneSuggestion.TYPE,
- typeProtoValue);
- if (isCertain()) {
- for (int zoneIdOrdinal : getZoneIdOrdinals()) {
- protoOutputStream.write(
- android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
- zoneIdOrdinal);
- }
- }
- protoOutputStream.flush();
- closeQuietly(byteArrayOutputStream);
- return byteArrayOutputStream.toByteArray();
- }
-
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 984b9ba..692b0cc 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -172,12 +172,13 @@
* Enables/disables the state recording mode for tests. The value is reset with {@link
* #resetVolatileTestConfig()}.
*/
- void setRecordProviderStateChanges(boolean enabled);
+ void setRecordStateChangesForTests(boolean enabled);
/**
- * Returns {@code true} if providers are expected to record their state changes for tests.
+ * Returns {@code true} if the controller / providers are expected to record their state changes
+ * for tests.
*/
- boolean getRecordProviderStateChanges();
+ boolean getRecordStateChangesForTests();
/**
* Returns the mode for the primary location time zone provider.
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
index 6e63f59..02ea433 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
@@ -64,6 +64,7 @@
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
+ ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
}));
/**
@@ -149,7 +150,7 @@
* See also {@link #resetVolatileTestConfig()}.
*/
@GuardedBy("this")
- private boolean mRecordProviderStateChanges;
+ private boolean mRecordStateChangesForTests;
private ServiceConfigAccessorImpl(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
@@ -281,8 +282,8 @@
// later releases that start to support geo detection on the same hardware.
if (!getGeoDetectionSettingEnabledOverride().isPresent()
&& isGeoTimeZoneDetectionFeatureSupported()) {
- final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
- setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
+ final boolean geoDetectionEnabledSetting = configuration.isGeoDetectionEnabled();
+ setGeoDetectionEnabledSettingIfRequired(userId, geoDetectionEnabledSetting);
}
}
}
@@ -294,10 +295,11 @@
.setTelephonyDetectionFeatureSupported(
isTelephonyTimeZoneDetectionFeatureSupported())
.setGeoDetectionFeatureSupported(isGeoTimeZoneDetectionFeatureSupported())
- .setAutoDetectionEnabled(isAutoDetectionEnabled())
+ .setTelephonyFallbackSupported(isTelephonyFallbackSupported())
+ .setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting())
.setUserConfigAllowed(isUserConfigAllowed(userId))
- .setLocationEnabled(isLocationEnabled(userId))
- .setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
+ .setLocationEnabledSetting(getLocationEnabledSetting(userId))
+ .setGeoDetectionEnabledSetting(getGeoDetectionEnabledSetting(userId))
.build();
}
@@ -306,12 +308,12 @@
// a ConfigurationChangeListener callback triggering due to ContentObserver's still
// triggering *sometimes* for no-op updates. Because callbacks are async this is necessary
// for stable behavior during tests.
- if (isAutoDetectionEnabled() != enabled) {
+ if (getAutoDetectionEnabledSetting() != enabled) {
Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, enabled ? 1 : 0);
}
}
- private boolean isLocationEnabled(@UserIdInt int userId) {
+ private boolean getLocationEnabledSetting(@UserIdInt int userId) {
return mLocationManager.isLocationEnabledForUser(UserHandle.of(userId));
}
@@ -320,11 +322,11 @@
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
}
- private boolean isAutoDetectionEnabled() {
+ private boolean getAutoDetectionEnabledSetting() {
return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
}
- private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
+ private boolean getGeoDetectionEnabledSetting(@UserIdInt int userId) {
// We may never use this, but it gives us a way to force location-based time zone detection
// on/off for testers (but only where their other settings would allow them to turn it on
// for themselves).
@@ -339,9 +341,9 @@
(geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
}
- private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
+ private void setGeoDetectionEnabledSettingIfRequired(@UserIdInt int userId, boolean enabled) {
// See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
- if (isGeoDetectionEnabled(userId) != enabled) {
+ if (getGeoDetectionEnabledSetting(userId) != enabled) {
Settings.Secure.putIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
enabled ? 1 : 0, userId);
}
@@ -451,13 +453,13 @@
}
@Override
- public synchronized void setRecordProviderStateChanges(boolean enabled) {
- mRecordProviderStateChanges = enabled;
+ public synchronized void setRecordStateChangesForTests(boolean enabled) {
+ mRecordStateChangesForTests = enabled;
}
@Override
- public synchronized boolean getRecordProviderStateChanges() {
- return mRecordProviderStateChanges;
+ public synchronized boolean getRecordStateChangesForTests() {
+ return mRecordStateChangesForTests;
}
@Override
@@ -546,7 +548,14 @@
mTestPrimaryLocationTimeZoneProviderMode = null;
mTestSecondaryLocationTimeZoneProviderPackageName = null;
mTestSecondaryLocationTimeZoneProviderMode = null;
- mRecordProviderStateChanges = false;
+ mRecordStateChangesForTests = false;
+ }
+
+ private boolean isTelephonyFallbackSupported() {
+ return mServerFlags.getBoolean(
+ ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
+ getConfigBoolean(
+ com.android.internal.R.bool.config_supportTelephonyTimeZoneFallback));
}
private boolean getConfigBoolean(int providerEnabledConfigId) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index e0c39ad..14784cf 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -83,6 +83,16 @@
ServiceConfigAccessorImpl.getInstance(context);
TimeZoneDetectorStrategy timeZoneDetectorStrategy =
TimeZoneDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);
+ DeviceActivityMonitor deviceActivityMonitor =
+ DeviceActivityMonitorImpl.create(context, handler);
+
+ // Wire up the telephony fallback behavior to activity detection.
+ deviceActivityMonitor.addListener(new DeviceActivityMonitor.Listener() {
+ @Override
+ public void onFlightComplete() {
+ timeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
+ }
+ });
// Create and publish the local service for use by internal callers.
TimeZoneDetectorInternal internal =
@@ -93,6 +103,10 @@
// permissioned) processes.
TimeZoneDetectorService service = TimeZoneDetectorService.create(
context, handler, serviceConfigAccessor, timeZoneDetectorStrategy);
+
+ // Dump the device activity monitor when the service is dumped.
+ service.addDumpable(deviceActivityMonitor);
+
publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
}
}
@@ -332,6 +346,15 @@
}
/**
+ * Sends a signal to enable telephony fallback. Provided for command-line access for use
+ * during tests. This is not exposed as a binder API.
+ */
+ void enableTelephonyFallback() {
+ enforceManageTimeZoneDetectorPermission();
+ mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
+ }
+
+ /**
* Registers the supplied {@link Dumpable} for dumping. When the service is dumped
* {@link Dumpable#dump(IndentingPrintWriter, String[])} will be called on the {@code dumpable}.
*/
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index a4a46a3..2b912ad 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -15,6 +15,7 @@
*/
package com.android.server.timezonedetector;
+import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED;
@@ -30,6 +31,7 @@
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED;
import android.app.time.LocationTimeZoneManager;
import android.app.time.TimeZoneConfiguration;
@@ -76,6 +78,8 @@
return runSuggestManualTimeZone();
case SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE:
return runSuggestTelephonyTimeZone();
+ case SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK:
+ return runEnableTelephonyFallback();
default: {
return handleDefaultCommands(cmd);
}
@@ -169,6 +173,11 @@
}
}
+ private int runEnableTelephonyFallback() {
+ mInterface.enableTelephonyFallback();
+ return 1;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -190,6 +199,13 @@
+ "\n");
pw.printf(" %s true|false\n", SHELL_COMMAND_SET_GEO_DETECTION_ENABLED);
pw.printf(" Sets the geolocation time zone detection enabled setting.\n");
+ pw.printf(" %s\n", SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK);
+ pw.printf(" Signals that telephony time zone detection fall back can be used if"
+ + " geolocation detection is supported and enabled. This is a temporary state until"
+ + " geolocation detection becomes \"certain\". To have an effect this requires that"
+ + " the telephony fallback feature is supported on the device, see below for"
+ + " for device_config flags.\n");
+ pw.println();
pw.printf(" %s <geolocation suggestion opts>\n",
SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE);
pw.printf(" %s <manual suggestion opts>\n",
@@ -216,6 +232,9 @@
pw.printf(" %s\n", KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE);
pw.printf(" Used to override the device's 'geolocation time zone detection enabled'"
+ " setting [*].\n");
+ pw.printf(" %s\n", KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED);
+ pw.printf(" Used to enable / disable support for telephony detection fallback. Also see"
+ + " the %s command.\n", SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK);
pw.println();
pw.printf("[*] To be enabled, the user must still have location = on / auto time zone"
+ " detection = on.\n");
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index ede52ba..6b04adf 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -69,6 +69,20 @@
* users enter areas without the necessary signals. Ultimately, with no perfect algorithm available,
* the user is left to choose which algorithm works best for their circumstances.
*
+ * <p>When geolocation detection is supported and enabled, in certain circumstances, such as during
+ * international travel, it makes sense to prioritize speed of detection via telephony (when
+ * available) Vs waiting for the geolocation algorithm to reach certainty. Geolocation detection can
+ * sometimes be slow to get a location fix and can require network connectivity (which cannot be
+ * assumed when users are travelling) for server-assisted location detection or time zone lookup.
+ * Therefore, as a restricted form of prioritization between geolocation and telephony algorithms,
+ * the strategy provides "telephony fallback" behavior, which can be set to "supported" via device
+ * config. Fallback mode is toggled on at runtime via {@link #enableTelephonyTimeZoneFallback()} in
+ * response to signals outside of the scope of this class. Telephony fallback allows the use of
+ * telephony suggestions to help with faster detection but only until geolocation detection
+ * provides a concrete, "certain" suggestion. After geolocation has made the first certain
+ * suggestion, telephony fallback is disabled until the next call to {@link
+ * #enableTelephonyTimeZoneFallback()}.
+ *
* <p>Threading:
*
* <p>Implementations of this class must be thread-safe as calls calls like {@link
@@ -100,6 +114,13 @@
*/
void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
+ /**
+ * Tells the strategy that it can fall back to telephony detection while geolocation detection
+ * remains uncertain. {@link #suggestGeolocationTimeZone(GeolocationTimeZoneSuggestion)} can
+ * disable it again. See {@link TimeZoneDetectorStrategy} for details.
+ */
+ void enableTelephonyTimeZoneFallback();
+
/** Generates a state snapshot for metrics. */
@NonNull
MetricsTimeZoneDetectorState generateMetricsState();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 3b6c1ea..6840511 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -22,6 +22,7 @@
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -31,6 +32,7 @@
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.content.Context;
import android.os.Handler;
+import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Slog;
@@ -38,6 +40,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.time.Duration;
import java.util.List;
import java.util.Objects;
@@ -83,6 +86,13 @@
* Sets the device's time zone.
*/
void setDeviceTimeZone(@NonNull String zoneId);
+
+ /**
+ * Returns the time according to the elapsed realtime clock, the same as {@link
+ * android.os.SystemClock#elapsedRealtime()}.
+ */
+ @ElapsedRealtimeLong
+ long elapsedRealtimeMillis();
}
private static final String LOG_TAG = TimeZoneDetectorService.TAG;
@@ -191,6 +201,21 @@
private ConfigurationInternal mCurrentConfigurationInternal;
/**
+ * Whether telephony time zone detection fallback is currently enabled (when device config also
+ * allows).
+ *
+ * <p>This field is only actually used when telephony time zone fallback is supported, but the
+ * value is maintained even when it isn't supported as it can be turned on at any time via
+ * server flags. The reference time is the elapsed realtime when the mode last changed to help
+ * ordering between fallback mode switches and suggestions.
+ *
+ * <p>See {@link TimeZoneDetectorStrategy} for more information.
+ */
+ @GuardedBy("this")
+ @NonNull
+ private TimestampedValue<Boolean> mTelephonyTimeZoneFallbackEnabled;
+
+ /**
* Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
*/
public static TimeZoneDetectorStrategyImpl create(
@@ -205,6 +230,10 @@
public TimeZoneDetectorStrategyImpl(@NonNull Environment environment) {
mEnvironment = Objects.requireNonNull(environment);
+ // Start with telephony fallback enabled.
+ mTelephonyTimeZoneFallbackEnabled =
+ new TimestampedValue<>(mEnvironment.elapsedRealtimeMillis(), true);
+
synchronized (this) {
mEnvironment.setConfigurationInternalChangeListener(
this::handleConfigurationInternalChanged);
@@ -233,6 +262,10 @@
// are made in a sensible order and the most recent is always the best one to use.
mLatestGeoLocationSuggestion.set(suggestion);
+ // Update the mTelephonyTimeZoneFallbackEnabled state if needed: a certain suggestion
+ // will usually disable telephony fallback mode if it is currently enabled.
+ disableTelephonyFallbackIfNeeded();
+
// Now perform auto time zone detection. The new suggestion may be used to modify the
// time zone setting.
String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
@@ -304,6 +337,44 @@
}
@Override
+ public synchronized void enableTelephonyTimeZoneFallback() {
+ // Only do any work if fallback is currently not enabled.
+ if (!mTelephonyTimeZoneFallbackEnabled.getValue()) {
+ ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal;
+ final boolean fallbackEnabled = true;
+ mTelephonyTimeZoneFallbackEnabled = new TimestampedValue<>(
+ mEnvironment.elapsedRealtimeMillis(), fallbackEnabled);
+
+ String logMsg = "enableTelephonyTimeZoneFallbackMode"
+ + ": currentUserConfig=" + currentUserConfig
+ + ", mTelephonyTimeZoneFallbackEnabled="
+ + mTelephonyTimeZoneFallbackEnabled;
+ logTimeZoneDetectorChange(logMsg);
+
+ // mTelephonyTimeZoneFallbackEnabled and mLatestGeoLocationSuggestion interact.
+ // If there is currently a certain geolocation suggestion, then the telephony fallback
+ // value needs to be considered after changing it.
+ // With the way that the mTelephonyTimeZoneFallbackEnabled time is currently chosen
+ // above, and the fact that geolocation suggestions should never have a time in the
+ // future, the following call will be a no-op, and telephony fallback will remain
+ // enabled. This comment / call is left as a reminder that it is possible for there to
+ // be a current, "certain" geolocation suggestion when this signal arrives and it is
+ // intentional that fallback stays enabled in this case. The choice to do this
+ // is mostly for symmetry WRT the case where fallback is enabled and an old "certain"
+ // geolocation is received; that would also leave telephony fallback enabled.
+ // This choice means that telephony fallback will remain enabled until a new "certain"
+ // geolocation suggestion is received. If, instead, the next geolocation is "uncertain",
+ // then telephony fallback will occur.
+ disableTelephonyFallbackIfNeeded();
+
+ if (currentUserConfig.isTelephonyFallbackSupported()) {
+ String reason = "enableTelephonyTimeZoneFallbackMode";
+ doAutoTimeZoneDetection(currentUserConfig, reason);
+ }
+ }
+ }
+
+ @Override
@NonNull
public synchronized MetricsTimeZoneDetectorState generateMetricsState() {
// Just capture one telephony suggestion: the one that would be used right now if telephony
@@ -361,7 +432,32 @@
// Use the correct algorithm based on the user's current configuration. If it changes, then
// detection will be re-run.
if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
- doGeolocationTimeZoneDetection(detectionReason);
+ boolean isGeoDetectionCertain = doGeolocationTimeZoneDetection(detectionReason);
+
+ // When geolocation detection is uncertain of the time zone, telephony detection
+ // can be used if telephony fallback is enabled and supported.
+ if (!isGeoDetectionCertain
+ && mTelephonyTimeZoneFallbackEnabled.getValue()
+ && currentUserConfig.isTelephonyFallbackSupported()) {
+
+ // This "only look at telephony if geolocation is uncertain" approach is
+ // deliberate to try to keep the logic simple and keep telephony and geolocation
+ // detection decoupled: when geolocation detection is in use, it is fully
+ // trusted and the most recent "certain" geolocation suggestion available will
+ // be used, even if the information it is based on is quite old.
+ // There could be newer telephony suggestions available, but telephony
+ // suggestions tend not to be withdrawn when they should be, and are based on
+ // combining information like MCC and NITZ signals, which could have been
+ // received at different times; thus it is hard to say what time the suggestion
+ // is actually "for" and reason clearly about ordering between telephony and
+ // geolocation suggestions.
+ //
+ // This approach is reliant on the location_time_zone_manager (and the location
+ // time zone providers it manages) correctly sending "uncertain" suggestions
+ // when the current location is unknown so that telephony fallback will actually be
+ // used.
+ doTelephonyTimeZoneDetection(detectionReason + ", telephony fallback mode");
+ }
} else {
doTelephonyTimeZoneDetection(detectionReason);
}
@@ -371,21 +467,29 @@
* Detects the time zone using the latest available geolocation time zone suggestion, if one is
* available. The outcome can be that this strategy becomes / remains un-opinionated and nothing
* is set.
+ *
+ * @return true if geolocation time zone detection was certain of the time zone, false if it is
+ * uncertain
*/
@GuardedBy("this")
- private void doGeolocationTimeZoneDetection(@NonNull String detectionReason) {
+ private boolean doGeolocationTimeZoneDetection(@NonNull String detectionReason) {
GeolocationTimeZoneSuggestion latestGeolocationSuggestion =
mLatestGeoLocationSuggestion.get();
if (latestGeolocationSuggestion == null) {
- return;
+ return false;
}
List<String> zoneIds = latestGeolocationSuggestion.getZoneIds();
- if (zoneIds == null || zoneIds.isEmpty()) {
- // This means the client has become uncertain about the time zone or it is certain there
- // is no known zone. In either case we must leave the existing time zone setting as it
- // is.
- return;
+ if (zoneIds == null) {
+ // This means the originator of the suggestion is uncertain about the time zone. The
+ // existing time zone setting must be left as it is but detection can go on looking for
+ // a different answer elsewhere.
+ return false;
+ } else if (zoneIds.isEmpty()) {
+ // This means the originator is certain there is no time zone. The existing time zone
+ // setting must be left as it is and detection must not go looking for a different
+ // answer elsewhere.
+ return true;
}
// GeolocationTimeZoneSuggestion has no measure of quality. We assume all suggestions are
@@ -404,6 +508,46 @@
zoneId = zoneIds.get(0);
}
setDeviceTimeZoneIfRequired(zoneId, detectionReason);
+ return true;
+ }
+
+ /**
+ * Sets the mTelephonyTimeZoneFallbackEnabled state to {@code false} if the latest geo
+ * suggestion is a "certain" suggestion that comes after the time when telephony fallback was
+ * enabled.
+ */
+ @GuardedBy("this")
+ private void disableTelephonyFallbackIfNeeded() {
+ GeolocationTimeZoneSuggestion suggestion = mLatestGeoLocationSuggestion.get();
+ boolean isLatestSuggestionCertain = suggestion != null && suggestion.getZoneIds() != null;
+ if (isLatestSuggestionCertain && mTelephonyTimeZoneFallbackEnabled.getValue()) {
+ // This transition ONLY changes mTelephonyTimeZoneFallbackEnabled from
+ // true -> false. See mTelephonyTimeZoneFallbackEnabled javadocs for details.
+
+ // Telephony fallback will be disabled after a "certain" suggestion is processed
+ // if and only if the location information it is based on is from after telephony
+ // fallback was enabled.
+ boolean latestSuggestionIsNewerThanFallbackEnabled =
+ suggestion.getEffectiveFromElapsedMillis()
+ > mTelephonyTimeZoneFallbackEnabled.getReferenceTimeMillis();
+ if (latestSuggestionIsNewerThanFallbackEnabled) {
+ final boolean fallbackEnabled = false;
+ mTelephonyTimeZoneFallbackEnabled = new TimestampedValue<>(
+ mEnvironment.elapsedRealtimeMillis(), fallbackEnabled);
+
+ String logMsg = "disableTelephonyFallbackIfNeeded"
+ + ": mTelephonyTimeZoneFallbackEnabled="
+ + mTelephonyTimeZoneFallbackEnabled;
+ logTimeZoneDetectorChange(logMsg);
+ }
+ }
+ }
+
+ private void logTimeZoneDetectorChange(@NonNull String logMsg) {
+ if (DBG) {
+ Slog.d(LOG_TAG, logMsg);
+ }
+ mTimeZoneChangesLog.log(logMsg);
}
/**
@@ -472,14 +616,11 @@
}
mEnvironment.setDeviceTimeZone(newZoneId);
- String msg = "Set device time zone."
+ String logMsg = "Set device time zone."
+ ", currentZoneId=" + currentZoneId
+ ", newZoneId=" + newZoneId
+ ", cause=" + cause;
- if (DBG) {
- Slog.d(LOG_TAG, msg);
- }
- mTimeZoneChangesLog.log(msg);
+ logTimeZoneDetectorChange(logMsg);
}
@GuardedBy("this")
@@ -531,9 +672,7 @@
String logMsg = "handleConfigurationInternalChanged:"
+ " oldConfiguration=" + mCurrentConfigurationInternal
+ ", newConfiguration=" + currentUserConfig;
- if (DBG) {
- Slog.d(LOG_TAG, logMsg);
- }
+ logTimeZoneDetectorChange(logMsg);
mCurrentConfigurationInternal = currentUserConfig;
// The configuration change may have changed available suggestions or the way suggestions
@@ -556,6 +695,12 @@
+ mEnvironment.isDeviceTimeZoneInitialized());
ipw.println("mEnvironment.getDeviceTimeZone()=" + mEnvironment.getDeviceTimeZone());
+ ipw.println("Misc state:");
+ ipw.increaseIndent(); // level 2
+ ipw.println("mTelephonyTimeZoneFallbackEnabled="
+ + formatDebugString(mTelephonyTimeZoneFallbackEnabled));
+ ipw.decreaseIndent(); // level 2
+
ipw.println("Time zone change log:");
ipw.increaseIndent(); // level 2
mTimeZoneChangesLog.dump(ipw);
@@ -603,6 +748,11 @@
return mLatestGeoLocationSuggestion.get();
}
+ @VisibleForTesting
+ public synchronized boolean isTelephonyFallbackEnabledForTests() {
+ return mTelephonyTimeZoneFallbackEnabled.getValue();
+ }
+
/**
* A {@link TelephonyTimeZoneSuggestion} with additional qualifying metadata.
*/
@@ -652,4 +802,8 @@
+ '}';
}
}
+
+ private static String formatDebugString(TimestampedValue<?> value) {
+ return value.getValue() + " @ " + Duration.ofMillis(value.getReferenceTimeMillis());
+ }
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
deleted file mode 100644
index b9da2eb..0000000
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
+++ /dev/null
@@ -1,670 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.timezonedetector.location;
-
-import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
-import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
-import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
-
-import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
-import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
-import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
-import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-
-import android.annotation.DurationMillisLong;
-import android.annotation.ElapsedRealtimeLong;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.service.timezone.TimeZoneProviderEvent;
-import android.service.timezone.TimeZoneProviderSuggestion;
-import android.util.IndentingPrintWriter;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.timezonedetector.ConfigurationInternal;
-import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
-import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
-
-import java.time.Duration;
-import java.util.Objects;
-
-/**
- * A real implementation of {@link LocationTimeZoneProviderController} that supports a primary and a
- * secondary {@link LocationTimeZoneProvider}.
- *
- * <p>The primary is used until it fails or becomes uncertain. The secondary will then be started.
- * The controller will immediately make suggestions based on "certain" {@link
- * TimeZoneProviderEvent}s, i.e. events that demonstrate the provider is certain what the time zone
- * is. The controller will not make immediate suggestions based on "uncertain" events, giving
- * providers time to change their mind. This also gives the secondary provider time to initialize
- * when the primary becomes uncertain.
- */
-class ControllerImpl extends LocationTimeZoneProviderController {
-
- @NonNull private final LocationTimeZoneProvider mPrimaryProvider;
-
- @NonNull private final LocationTimeZoneProvider mSecondaryProvider;
-
- @GuardedBy("mSharedLock")
- // Non-null after initialize()
- private ConfigurationInternal mCurrentUserConfiguration;
-
- @GuardedBy("mSharedLock")
- // Non-null after initialize()
- private Environment mEnvironment;
-
- @GuardedBy("mSharedLock")
- // Non-null after initialize()
- private Callback mCallback;
-
- /** Indicates both providers have completed initialization. */
- @GuardedBy("mSharedLock")
- private boolean mProvidersInitialized;
-
- /**
- * Used for scheduling uncertainty timeouts, i.e after a provider has reported uncertainty.
- * This timeout is not provider-specific: it is started when the controller becomes uncertain
- * due to events it has received from one or other provider.
- */
- @NonNull private final SingleRunnableQueue mUncertaintyTimeoutQueue;
-
- /** Contains the last suggestion actually made, if there is one. */
- @GuardedBy("mSharedLock")
- @Nullable
- private GeolocationTimeZoneSuggestion mLastSuggestion;
-
- ControllerImpl(@NonNull ThreadingDomain threadingDomain,
- @NonNull LocationTimeZoneProvider primaryProvider,
- @NonNull LocationTimeZoneProvider secondaryProvider) {
- super(threadingDomain);
- mUncertaintyTimeoutQueue = threadingDomain.createSingleRunnableQueue();
- mPrimaryProvider = Objects.requireNonNull(primaryProvider);
- mSecondaryProvider = Objects.requireNonNull(secondaryProvider);
- }
-
- @Override
- void initialize(@NonNull Environment environment, @NonNull Callback callback) {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- debugLog("initialize()");
- mEnvironment = Objects.requireNonNull(environment);
- mCallback = Objects.requireNonNull(callback);
- mCurrentUserConfiguration = environment.getCurrentUserConfigurationInternal();
-
- LocationTimeZoneProvider.ProviderListener providerListener =
- ControllerImpl.this::onProviderStateChange;
- mPrimaryProvider.initialize(providerListener);
- mSecondaryProvider.initialize(providerListener);
- mProvidersInitialized = true;
-
- alterProvidersStartedStateIfRequired(
- null /* oldConfiguration */, mCurrentUserConfiguration);
- }
- }
-
- @Override
- void onConfigurationInternalChanged() {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- debugLog("onConfigChanged()");
-
- ConfigurationInternal oldConfig = mCurrentUserConfiguration;
- ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal();
- mCurrentUserConfiguration = newConfig;
-
- if (!newConfig.equals(oldConfig)) {
- if (newConfig.getUserId() != oldConfig.getUserId()) {
- // If the user changed, stop the providers if needed. They may be re-started
- // for the new user immediately afterwards if their settings allow.
- debugLog("User changed. old=" + oldConfig.getUserId()
- + ", new=" + newConfig.getUserId() + ": Stopping providers");
- stopProviders();
-
- alterProvidersStartedStateIfRequired(null /* oldConfiguration */, newConfig);
- } else {
- alterProvidersStartedStateIfRequired(oldConfig, newConfig);
- }
- }
- }
- }
-
- @Override
- boolean isUncertaintyTimeoutSet() {
- return mUncertaintyTimeoutQueue.hasQueued();
- }
-
- @Override
- @DurationMillisLong
- long getUncertaintyTimeoutDelayMillis() {
- return mUncertaintyTimeoutQueue.getQueuedDelayMillis();
- }
-
- @Override
- void destroy() {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- stopProviders();
- mPrimaryProvider.destroy();
- mSecondaryProvider.destroy();
- }
- }
-
- @GuardedBy("mSharedLock")
- private void stopProviders() {
- stopProviderIfStarted(mPrimaryProvider);
- stopProviderIfStarted(mSecondaryProvider);
-
- // By definition, if both providers are stopped, the controller is uncertain.
- cancelUncertaintyTimeout();
-
- // If a previous "certain" suggestion has been made, then a new "uncertain"
- // suggestion must now be made to indicate the controller {does not / no longer has}
- // an opinion and will not be sending further updates (until at least the providers are
- // re-started).
- if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- mEnvironment.elapsedRealtimeMillis(), "Providers are stopping");
- makeSuggestion(suggestion);
- }
- }
-
- @GuardedBy("mSharedLock")
- private void stopProviderIfStarted(@NonNull LocationTimeZoneProvider provider) {
- if (provider.getCurrentState().isStarted()) {
- stopProvider(provider);
- }
- }
-
- @GuardedBy("mSharedLock")
- private void stopProvider(@NonNull LocationTimeZoneProvider provider) {
- ProviderState providerState = provider.getCurrentState();
- switch (providerState.stateEnum) {
- case PROVIDER_STATE_STOPPED: {
- debugLog("No need to stop " + provider + ": already stopped");
- break;
- }
- case PROVIDER_STATE_STARTED_INITIALIZING:
- case PROVIDER_STATE_STARTED_CERTAIN:
- case PROVIDER_STATE_STARTED_UNCERTAIN: {
- debugLog("Stopping " + provider);
- provider.stopUpdates();
- break;
- }
- case PROVIDER_STATE_PERM_FAILED:
- case PROVIDER_STATE_DESTROYED: {
- debugLog("Unable to stop " + provider + ": it is terminated.");
- break;
- }
- default: {
- warnLog("Unknown provider state: " + provider);
- break;
- }
- }
- }
-
- /**
- * Sets the providers into the correct started/stopped state for the {@code newConfiguration}
- * and, if there is a provider state change, makes any suggestions required to inform the
- * downstream time zone detection code.
- *
- * <p>This is a utility method that exists to avoid duplicated logic for the various cases when
- * provider started / stopped state may need to be set or changed, e.g. during initialization
- * or when a new configuration has been received.
- */
- @GuardedBy("mSharedLock")
- private void alterProvidersStartedStateIfRequired(
- @Nullable ConfigurationInternal oldConfiguration,
- @NonNull ConfigurationInternal newConfiguration) {
-
- // Provider started / stopped states only need to be changed if geoDetectionEnabled has
- // changed.
- boolean oldGeoDetectionEnabled = oldConfiguration != null
- && oldConfiguration.getGeoDetectionEnabledBehavior();
- boolean newGeoDetectionEnabled = newConfiguration.getGeoDetectionEnabledBehavior();
- if (oldGeoDetectionEnabled == newGeoDetectionEnabled) {
- return;
- }
-
- // The check above ensures that the logic below only executes if providers are going from
- // {started *} -> {stopped}, or {stopped} -> {started initializing}. If this changes in
- // future and there could be {started *} -> {started *} cases, or cases where the provider
- // can't be assumed to go straight to the {started initializing} state, then the logic below
- // would need to cover extra conditions, for example:
- // 1) If the primary is in {started uncertain}, the secondary should be started.
- // 2) If (1), and the secondary instantly enters the {perm failed} state, the uncertainty
- // timeout started when the primary entered {started uncertain} should be cancelled.
-
- if (newGeoDetectionEnabled) {
- // Try to start the primary provider.
- tryStartProvider(mPrimaryProvider, newConfiguration);
-
- // The secondary should only ever be started if the primary now isn't started (i.e. it
- // couldn't become {started initializing} because it is {perm failed}).
- ProviderState newPrimaryState = mPrimaryProvider.getCurrentState();
- if (!newPrimaryState.isStarted()) {
- // If the primary provider is {perm failed} then the controller must try to start
- // the secondary.
- tryStartProvider(mSecondaryProvider, newConfiguration);
-
- ProviderState newSecondaryState = mSecondaryProvider.getCurrentState();
- if (!newSecondaryState.isStarted()) {
- // If both providers are {perm failed} then the controller immediately
- // becomes uncertain.
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- mEnvironment.elapsedRealtimeMillis(),
- "Providers are failed:"
- + " primary=" + mPrimaryProvider.getCurrentState()
- + " secondary=" + mPrimaryProvider.getCurrentState());
- makeSuggestion(suggestion);
- }
- }
- } else {
- stopProviders();
- }
- }
-
- @GuardedBy("mSharedLock")
- private void tryStartProvider(@NonNull LocationTimeZoneProvider provider,
- @NonNull ConfigurationInternal configuration) {
- ProviderState providerState = provider.getCurrentState();
- switch (providerState.stateEnum) {
- case PROVIDER_STATE_STOPPED: {
- debugLog("Enabling " + provider);
- provider.startUpdates(configuration,
- mEnvironment.getProviderInitializationTimeout(),
- mEnvironment.getProviderInitializationTimeoutFuzz(),
- mEnvironment.getProviderEventFilteringAgeThreshold());
- break;
- }
- case PROVIDER_STATE_STARTED_INITIALIZING:
- case PROVIDER_STATE_STARTED_CERTAIN:
- case PROVIDER_STATE_STARTED_UNCERTAIN: {
- debugLog("No need to start " + provider + ": already started");
- break;
- }
- case PROVIDER_STATE_PERM_FAILED:
- case PROVIDER_STATE_DESTROYED: {
- debugLog("Unable to start " + provider + ": it is terminated");
- break;
- }
- default: {
- throw new IllegalStateException("Unknown provider state:"
- + " provider=" + provider);
- }
- }
- }
-
- void onProviderStateChange(@NonNull ProviderState providerState) {
- mThreadingDomain.assertCurrentThread();
- LocationTimeZoneProvider provider = providerState.provider;
- assertProviderKnown(provider);
-
- synchronized (mSharedLock) {
- // Ignore provider state changes during initialization. e.g. if the primary provider
- // moves to PROVIDER_STATE_PERM_FAILED during initialization, the secondary will not
- // be ready to take over yet.
- if (!mProvidersInitialized) {
- warnLog("onProviderStateChange: Ignoring provider state change because both"
- + " providers have not yet completed initialization."
- + " providerState=" + providerState);
- return;
- }
-
- switch (providerState.stateEnum) {
- case PROVIDER_STATE_STARTED_INITIALIZING:
- case PROVIDER_STATE_STOPPED:
- case PROVIDER_STATE_DESTROYED: {
- // This should never happen: entering initializing, stopped or destroyed are
- // triggered by the controller so and should not trigger a state change
- // callback.
- warnLog("onProviderStateChange: Unexpected state change for provider,"
- + " provider=" + provider);
- break;
- }
- case PROVIDER_STATE_STARTED_CERTAIN:
- case PROVIDER_STATE_STARTED_UNCERTAIN: {
- // These are valid and only happen if an event is received while the provider is
- // started.
- debugLog("onProviderStateChange: Received notification of a state change while"
- + " started, provider=" + provider);
- handleProviderStartedStateChange(providerState);
- break;
- }
- case PROVIDER_STATE_PERM_FAILED: {
- debugLog("Received notification of permanent failure for"
- + " provider=" + provider);
- handleProviderFailedStateChange(providerState);
- break;
- }
- default: {
- warnLog("onProviderStateChange: Unexpected provider=" + provider);
- }
- }
- }
- }
-
- private void assertProviderKnown(@NonNull LocationTimeZoneProvider provider) {
- if (provider != mPrimaryProvider && provider != mSecondaryProvider) {
- throw new IllegalArgumentException("Unknown provider: " + provider);
- }
- }
-
- /**
- * Called when a provider has reported that it has failed permanently.
- */
- @GuardedBy("mSharedLock")
- private void handleProviderFailedStateChange(@NonNull ProviderState providerState) {
- LocationTimeZoneProvider failedProvider = providerState.provider;
- ProviderState primaryCurrentState = mPrimaryProvider.getCurrentState();
- ProviderState secondaryCurrentState = mSecondaryProvider.getCurrentState();
-
- // If a provider has failed, the other may need to be started.
- if (failedProvider == mPrimaryProvider) {
- if (!secondaryCurrentState.isTerminated()) {
- // Try to start the secondary. This does nothing if the provider is already
- // started, and will leave the provider in {started initializing} if the provider is
- // stopped.
- tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration);
- }
- } else if (failedProvider == mSecondaryProvider) {
- // No-op: The secondary will only be active if the primary is uncertain or is
- // terminated. So, there the primary should not need to be started when the secondary
- // fails.
- if (primaryCurrentState.stateEnum != PROVIDER_STATE_STARTED_UNCERTAIN
- && !primaryCurrentState.isTerminated()) {
- warnLog("Secondary provider unexpected reported a failure:"
- + " failed provider=" + failedProvider.getName()
- + ", primary provider=" + mPrimaryProvider
- + ", secondary provider=" + mSecondaryProvider);
- }
- }
-
- // If both providers are now terminated, the controller needs to tell the next component in
- // the time zone detection process.
- if (primaryCurrentState.isTerminated() && secondaryCurrentState.isTerminated()) {
-
- // If both providers are newly terminated then the controller is uncertain by definition
- // and it will never recover so it can send a suggestion immediately.
- cancelUncertaintyTimeout();
-
- // If both providers are now terminated, then a suggestion must be sent informing the
- // time zone detector that there are no further updates coming in future.
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- mEnvironment.elapsedRealtimeMillis(),
- "Both providers are terminated:"
- + " primary=" + primaryCurrentState.provider
- + ", secondary=" + secondaryCurrentState.provider);
- makeSuggestion(suggestion);
- }
- }
-
- /**
- * Called when a provider has changed state but just moved from one started state to another
- * started state, usually as a result of a new {@link TimeZoneProviderEvent} being received.
- * However, there are rare cases where the event can also be null.
- */
- @GuardedBy("mSharedLock")
- private void handleProviderStartedStateChange(@NonNull ProviderState providerState) {
- LocationTimeZoneProvider provider = providerState.provider;
- TimeZoneProviderEvent event = providerState.event;
- if (event == null) {
- // Implicit uncertainty, i.e. where the provider is started, but a problem has been
- // detected without having received an event. For example, if the process has detected
- // the loss of a binder-based provider, or initialization took too long. This is treated
- // the same as explicit uncertainty, i.e. where the provider has explicitly told this
- // process it is uncertain.
- long uncertaintyStartedElapsedMillis = mEnvironment.elapsedRealtimeMillis();
- handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis,
- "provider=" + provider + ", implicit uncertainty, event=null");
- return;
- }
-
- if (!mCurrentUserConfiguration.getGeoDetectionEnabledBehavior()) {
- // This should not happen: the provider should not be in an started state if the user
- // does not have geodetection enabled.
- warnLog("Provider=" + provider + " is started, but"
- + " currentUserConfiguration=" + mCurrentUserConfiguration
- + " suggests it shouldn't be.");
- }
-
- switch (event.getType()) {
- case EVENT_TYPE_PERMANENT_FAILURE: {
- // This shouldn't happen. A provider cannot be started and have this event type.
- warnLog("Provider=" + provider + " is started, but event suggests it shouldn't be");
- break;
- }
- case EVENT_TYPE_UNCERTAIN: {
- long uncertaintyStartedElapsedMillis = event.getCreationElapsedMillis();
- handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis,
- "provider=" + provider + ", explicit uncertainty. event=" + event);
- break;
- }
- case EVENT_TYPE_SUGGESTION: {
- handleProviderSuggestion(provider, event);
- break;
- }
- default: {
- warnLog("Unknown eventType=" + event.getType());
- break;
- }
- }
- }
-
- /**
- * Called when a provider has become "certain" about the time zone(s).
- */
- @GuardedBy("mSharedLock")
- private void handleProviderSuggestion(
- @NonNull LocationTimeZoneProvider provider,
- @NonNull TimeZoneProviderEvent providerEvent) {
-
- // By definition, the controller is now certain.
- cancelUncertaintyTimeout();
-
- if (provider == mPrimaryProvider) {
- stopProviderIfStarted(mSecondaryProvider);
- }
-
- TimeZoneProviderSuggestion providerSuggestion = providerEvent.getSuggestion();
-
- // For the suggestion's effectiveFromElapsedMillis, use the time embedded in the provider's
- // suggestion (which indicates the time when the provider detected the location used to
- // establish the time zone).
- //
- // An alternative would be to use the current time or the providerEvent creation time, but
- // this would hinder the ability for the time_zone_detector to judge which suggestions are
- // based on newer information when comparing suggestions between different sources.
- long effectiveFromElapsedMillis = providerSuggestion.getElapsedRealtimeMillis();
- GeolocationTimeZoneSuggestion geoSuggestion =
- GeolocationTimeZoneSuggestion.createCertainSuggestion(
- effectiveFromElapsedMillis, providerSuggestion.getTimeZoneIds());
-
- String debugInfo = "Event received provider=" + provider
- + ", providerEvent=" + providerEvent
- + ", suggestionCreationTime=" + mEnvironment.elapsedRealtimeMillis();
- geoSuggestion.addDebugInfo(debugInfo);
- makeSuggestion(geoSuggestion);
- }
-
- @Override
- public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
- synchronized (mSharedLock) {
- ipw.println("LocationTimeZoneProviderController:");
-
- ipw.increaseIndent(); // level 1
- ipw.println("mCurrentUserConfiguration=" + mCurrentUserConfiguration);
- ipw.println("providerInitializationTimeout="
- + mEnvironment.getProviderInitializationTimeout());
- ipw.println("providerInitializationTimeoutFuzz="
- + mEnvironment.getProviderInitializationTimeoutFuzz());
- ipw.println("uncertaintyDelay=" + mEnvironment.getUncertaintyDelay());
- ipw.println("mLastSuggestion=" + mLastSuggestion);
-
- ipw.println("Primary Provider:");
- ipw.increaseIndent(); // level 2
- mPrimaryProvider.dump(ipw, args);
- ipw.decreaseIndent(); // level 2
-
- ipw.println("Secondary Provider:");
- ipw.increaseIndent(); // level 2
- mSecondaryProvider.dump(ipw, args);
- ipw.decreaseIndent(); // level 2
-
- ipw.decreaseIndent(); // level 1
- }
- }
-
- /** Sends an immediate suggestion, updating mLastSuggestion. */
- @GuardedBy("mSharedLock")
- private void makeSuggestion(@NonNull GeolocationTimeZoneSuggestion suggestion) {
- debugLog("makeSuggestion: suggestion=" + suggestion);
- mCallback.suggest(suggestion);
- mLastSuggestion = suggestion;
- }
-
- /** Clears the uncertainty timeout. */
- @GuardedBy("mSharedLock")
- private void cancelUncertaintyTimeout() {
- mUncertaintyTimeoutQueue.cancel();
- }
-
- /**
- * Called when a provider has become "uncertain" about the time zone.
- *
- * <p>A provider is expected to report its uncertainty as soon as it becomes uncertain, as
- * this enables the most flexibility for the controller to start other providers when there are
- * multiple ones available. The controller is therefore responsible for deciding when to make a
- * "uncertain" suggestion to the downstream time zone detector.
- *
- * <p>This method schedules an "uncertainty" timeout (if one isn't already scheduled) to be
- * triggered later if nothing else preempts it. It can be preempted if the provider becomes
- * certain (or does anything else that calls {@link
- * #makeSuggestion(GeolocationTimeZoneSuggestion)}) within {@link
- * Environment#getUncertaintyDelay()}. Preemption causes the scheduled
- * "uncertainty" timeout to be cancelled. If the provider repeatedly sends uncertainty events
- * within the uncertainty delay period, those events are effectively ignored (i.e. the timeout
- * is not reset each time).
- */
- @GuardedBy("mSharedLock")
- void handleProviderUncertainty(
- @NonNull LocationTimeZoneProvider provider,
- @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis,
- @NonNull String reason) {
- Objects.requireNonNull(provider);
-
- // Start the uncertainty timeout if needed to ensure the controller will eventually make an
- // uncertain suggestion if no success event arrives in time to counteract it.
- if (!mUncertaintyTimeoutQueue.hasQueued()) {
- debugLog("Starting uncertainty timeout: reason=" + reason);
-
- Duration uncertaintyDelay = mEnvironment.getUncertaintyDelay();
- mUncertaintyTimeoutQueue.runDelayed(
- () -> onProviderUncertaintyTimeout(
- provider, uncertaintyStartedElapsedMillis, uncertaintyDelay),
- uncertaintyDelay.toMillis());
- }
-
- if (provider == mPrimaryProvider) {
- // (Try to) start the secondary. It could already be started, or enabling might not
- // succeed if the provider has previously reported it is perm failed. The uncertainty
- // timeout (set above) is used to ensure that an uncertain suggestion will be made if
- // the secondary cannot generate a success event in time.
- tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration);
- }
- }
-
- private void onProviderUncertaintyTimeout(
- @NonNull LocationTimeZoneProvider provider,
- @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis,
- @NonNull Duration uncertaintyDelay) {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- long afterUncertaintyTimeoutElapsedMillis = mEnvironment.elapsedRealtimeMillis();
-
- // For the effectiveFromElapsedMillis suggestion property, use the
- // uncertaintyStartedElapsedMillis. This is the time when the provider first reported
- // uncertainty, i.e. before the uncertainty timeout.
- //
- // afterUncertaintyTimeoutElapsedMillis could be used instead, which is the time when
- // the location_time_zone_manager finally confirms that the time zone was uncertain,
- // but the suggestion property allows the information to be back-dated, which should
- // help when comparing suggestions from different sources.
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- uncertaintyStartedElapsedMillis,
- "Uncertainty timeout triggered for " + provider.getName() + ":"
- + " primary=" + mPrimaryProvider
- + ", secondary=" + mSecondaryProvider
- + ", uncertaintyStarted="
- + Duration.ofMillis(uncertaintyStartedElapsedMillis)
- + ", afterUncertaintyTimeout="
- + Duration.ofMillis(afterUncertaintyTimeoutElapsedMillis)
- + ", uncertaintyDelay=" + uncertaintyDelay
- );
- makeSuggestion(suggestion);
- }
- }
-
- @NonNull
- private static GeolocationTimeZoneSuggestion createUncertainSuggestion(
- @ElapsedRealtimeLong long effectiveFromElapsedMillis,
- @NonNull String reason) {
- GeolocationTimeZoneSuggestion suggestion =
- GeolocationTimeZoneSuggestion.createUncertainSuggestion(
- effectiveFromElapsedMillis);
- suggestion.addDebugInfo(reason);
- return suggestion;
- }
-
- /**
- * Clears recorded provider state changes (for use during tests).
- */
- void clearRecordedProviderStates() {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- mPrimaryProvider.clearRecordedStates();
- mSecondaryProvider.clearRecordedStates();
- }
- }
-
- /**
- * Returns a snapshot of the current controller state for tests.
- */
- @NonNull
- LocationTimeZoneManagerServiceState getStateForTests() {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- LocationTimeZoneManagerServiceState.Builder builder =
- new LocationTimeZoneManagerServiceState.Builder();
- if (mLastSuggestion != null) {
- builder.setLastSuggestion(mLastSuggestion);
- }
- builder.setPrimaryProviderStateChanges(mPrimaryProvider.getRecordedStates())
- .setSecondaryProviderStateChanges(mSecondaryProvider.getRecordedStates());
- return builder.build();
- }
- }
-}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index ddbeac4..b23f11a 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -147,16 +147,16 @@
/** The shared lock from {@link #mThreadingDomain}. */
@NonNull private final Object mSharedLock;
- @NonNull
- private final ServiceConfigAccessor mServiceConfigAccessor;
+ @NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
// Lazily initialized. Can be null if the service has been stopped.
@GuardedBy("mSharedLock")
- private ControllerImpl mLocationTimeZoneDetectorController;
+ private LocationTimeZoneProviderController mLocationTimeZoneProviderController;
// Lazily initialized. Can be null if the service has been stopped.
@GuardedBy("mSharedLock")
- private ControllerEnvironmentImpl mEnvironment;
+ private LocationTimeZoneProviderControllerEnvironmentImpl
+ mLocationTimeZoneProviderControllerEnvironment;
LocationTimeZoneManagerService(@NonNull Context context,
@NonNull ServiceConfigAccessor serviceConfigAccessor) {
@@ -190,7 +190,7 @@
// Avoid starting the service if it is currently stopped. This is required because
// server flags are used by tests to set behavior with the service stopped, and we don't
// want the service being restarted after each flag is set.
- if (mLocationTimeZoneDetectorController != null) {
+ if (mLocationTimeZoneProviderController != null) {
// Stop and start the service, waiting until completion.
stopOnDomainThread();
startOnDomainThread();
@@ -247,8 +247,7 @@
* completion, it cannot be called from the {@code mThreadingDomain} thread.
*/
void startWithTestProviders(@Nullable String testPrimaryProviderPackageName,
- @Nullable String testSecondaryProviderPackageName,
- boolean recordProviderStateChanges) {
+ @Nullable String testSecondaryProviderPackageName, boolean recordStateChanges) {
enforceManageTimeZoneDetectorPermission();
if (testPrimaryProviderPackageName == null && testSecondaryProviderPackageName == null) {
@@ -263,7 +262,7 @@
testPrimaryProviderPackageName);
mServiceConfigAccessor.setTestSecondaryLocationTimeZoneProviderPackageName(
testSecondaryProviderPackageName);
- mServiceConfigAccessor.setRecordProviderStateChanges(recordProviderStateChanges);
+ mServiceConfigAccessor.setRecordStateChangesForTests(recordStateChanges);
startOnDomainThread();
}
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
@@ -278,19 +277,32 @@
return;
}
- if (mLocationTimeZoneDetectorController == null) {
+ if (mLocationTimeZoneProviderController == null) {
LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider();
LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider();
+ LocationTimeZoneProviderController.MetricsLogger metricsLogger =
+ new LocationTimeZoneProviderController.MetricsLogger() {
+ @Override
+ public void onStateChange(
+ @LocationTimeZoneProviderController.State String state) {
+ // TODO b/200279201 - wire this up to metrics code
+ // No-op.
+ }
+ };
- ControllerImpl controller =
- new ControllerImpl(mThreadingDomain, primary, secondary);
- ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl(
- mThreadingDomain, mServiceConfigAccessor, controller);
- ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
+ boolean recordStateChanges = mServiceConfigAccessor.getRecordStateChangesForTests();
+ LocationTimeZoneProviderController controller =
+ new LocationTimeZoneProviderController(mThreadingDomain, metricsLogger,
+ primary, secondary, recordStateChanges);
+ LocationTimeZoneProviderControllerEnvironmentImpl environment =
+ new LocationTimeZoneProviderControllerEnvironmentImpl(
+ mThreadingDomain, mServiceConfigAccessor, controller);
+ LocationTimeZoneProviderControllerCallbackImpl callback =
+ new LocationTimeZoneProviderControllerCallbackImpl(mThreadingDomain);
controller.initialize(environment, callback);
- mEnvironment = environment;
- mLocationTimeZoneDetectorController = controller;
+ mLocationTimeZoneProviderControllerEnvironment = environment;
+ mLocationTimeZoneProviderController = controller;
}
}
}
@@ -312,11 +324,11 @@
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
- if (mLocationTimeZoneDetectorController != null) {
- mLocationTimeZoneDetectorController.destroy();
- mLocationTimeZoneDetectorController = null;
- mEnvironment.destroy();
- mEnvironment = null;
+ if (mLocationTimeZoneProviderController != null) {
+ mLocationTimeZoneProviderController.destroy();
+ mLocationTimeZoneProviderController = null;
+ mLocationTimeZoneProviderControllerEnvironment.destroy();
+ mLocationTimeZoneProviderControllerEnvironment = null;
// Clear test state so it won't be used the next time the service is started.
mServiceConfigAccessor.resetVolatileTestConfig();
@@ -338,8 +350,8 @@
mThreadingDomain.postAndWait(() -> {
synchronized (mSharedLock) {
- if (mLocationTimeZoneDetectorController != null) {
- mLocationTimeZoneDetectorController.clearRecordedProviderStates();
+ if (mLocationTimeZoneProviderController != null) {
+ mLocationTimeZoneProviderController.clearRecordedStates();
}
}
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
@@ -357,10 +369,10 @@
return mThreadingDomain.postAndWait(
() -> {
synchronized (mSharedLock) {
- if (mLocationTimeZoneDetectorController == null) {
+ if (mLocationTimeZoneProviderController == null) {
return null;
}
- return mLocationTimeZoneDetectorController.getStateForTests();
+ return mLocationTimeZoneProviderController.getStateForTests();
}
},
BLOCKING_OP_WAIT_DURATION_MILLIS);
@@ -390,10 +402,10 @@
mSecondaryProviderConfig.dump(ipw, args);
ipw.decreaseIndent();
- if (mLocationTimeZoneDetectorController == null) {
+ if (mLocationTimeZoneProviderController == null) {
ipw.println("{Stopped}");
} else {
- mLocationTimeZoneDetectorController.dump(ipw, args);
+ mLocationTimeZoneProviderController.dump(ipw, args);
}
ipw.decreaseIndent();
}
@@ -447,10 +459,9 @@
ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(mIndex);
return new BinderLocationTimeZoneProvider(
providerMetricsLogger, mThreadingDomain, mName, proxy,
- mServiceConfigAccessor.getRecordProviderStateChanges());
+ mServiceConfigAccessor.getRecordStateChangesForTests());
}
- @GuardedBy("mSharedLock")
@Override
public void dump(IndentingPrintWriter ipw, String[] args) {
ipw.printf("getMode()=%s\n", getMode());
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
index 113926a..1f752f4 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
@@ -21,6 +21,7 @@
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
+import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;
import java.util.ArrayList;
import java.util.Collections;
@@ -30,22 +31,35 @@
/** A snapshot of the location time zone manager service's state for tests. */
final class LocationTimeZoneManagerServiceState {
+ private final @State String mControllerState;
@Nullable private final GeolocationTimeZoneSuggestion mLastSuggestion;
+ @NonNull private final List<@State String> mControllerStates;
@NonNull private final List<ProviderState> mPrimaryProviderStates;
@NonNull private final List<ProviderState> mSecondaryProviderStates;
LocationTimeZoneManagerServiceState(@NonNull Builder builder) {
+ mControllerState = builder.mControllerState;
mLastSuggestion = builder.mLastSuggestion;
+ mControllerStates = Objects.requireNonNull(builder.mControllerStates);
mPrimaryProviderStates = Objects.requireNonNull(builder.mPrimaryProviderStates);
mSecondaryProviderStates = Objects.requireNonNull(builder.mSecondaryProviderStates);
}
+ public @State String getControllerState() {
+ return mControllerState;
+ }
+
@Nullable
public GeolocationTimeZoneSuggestion getLastSuggestion() {
return mLastSuggestion;
}
@NonNull
+ public List<@State String> getControllerStates() {
+ return mControllerStates;
+ }
+
+ @NonNull
public List<ProviderState> getPrimaryProviderStates() {
return Collections.unmodifiableList(mPrimaryProviderStates);
}
@@ -58,7 +72,9 @@
@Override
public String toString() {
return "LocationTimeZoneManagerServiceState{"
- + "mLastSuggestion=" + mLastSuggestion
+ + "mControllerState=" + mControllerState
+ + ", mLastSuggestion=" + mLastSuggestion
+ + ", mControllerStates=" + mControllerStates
+ ", mPrimaryProviderStates=" + mPrimaryProviderStates
+ ", mSecondaryProviderStates=" + mSecondaryProviderStates
+ '}';
@@ -66,17 +82,31 @@
static final class Builder {
+ private @State String mControllerState;
private GeolocationTimeZoneSuggestion mLastSuggestion;
+ private List<@State String> mControllerStates;
private List<ProviderState> mPrimaryProviderStates;
private List<ProviderState> mSecondaryProviderStates;
@NonNull
+ public Builder setControllerState(@State String stateEnum) {
+ mControllerState = stateEnum;
+ return this;
+ }
+
+ @NonNull
Builder setLastSuggestion(@NonNull GeolocationTimeZoneSuggestion lastSuggestion) {
mLastSuggestion = Objects.requireNonNull(lastSuggestion);
return this;
}
@NonNull
+ public Builder setStateChanges(@NonNull List<@State String> states) {
+ mControllerStates = new ArrayList<>(states);
+ return this;
+ }
+
+ @NonNull
Builder setPrimaryProviderStateChanges(@NonNull List<ProviderState> primaryProviderStates) {
mPrimaryProviderStates = new ArrayList<>(primaryProviderStates);
return this;
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index 6c9e174..60bbea7 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -40,6 +40,14 @@
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_PROVIDERS_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,6 +63,7 @@
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
+import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -245,6 +254,7 @@
outputStream.end(lastSuggestionToken);
}
+ writeControllerStates(outputStream, state.getControllerStates());
writeProviderStates(outputStream, state.getPrimaryProviderStates(),
"primary_provider_states",
LocationTimeZoneManagerServiceStateProto.PRIMARY_PROVIDER_STATES);
@@ -256,6 +266,37 @@
return 0;
}
+ private static void writeControllerStates(DualDumpOutputStream outputStream,
+ List<@State String> states) {
+ for (@State String state : states) {
+ outputStream.write("controller_states",
+ LocationTimeZoneManagerServiceStateProto.CONTROLLER_STATES,
+ convertControllerStateToProtoEnum(state));
+ }
+ }
+
+ private static int convertControllerStateToProtoEnum(@State String state) {
+ switch (state) {
+ case STATE_PROVIDERS_INITIALIZING:
+ return LocationTimeZoneManagerProto.CONTROLLER_STATE_PROVIDERS_INITIALIZING;
+ case STATE_STOPPED:
+ return LocationTimeZoneManagerProto.CONTROLLER_STATE_STOPPED;
+ case STATE_INITIALIZING:
+ return LocationTimeZoneManagerProto.CONTROLLER_STATE_INITIALIZING;
+ case STATE_UNCERTAIN:
+ return LocationTimeZoneManagerProto.CONTROLLER_STATE_UNCERTAIN;
+ case STATE_CERTAIN:
+ return LocationTimeZoneManagerProto.CONTROLLER_STATE_CERTAIN;
+ case STATE_FAILED:
+ return LocationTimeZoneManagerProto.CONTROLLER_STATE_FAILED;
+ case STATE_DESTROYED:
+ return LocationTimeZoneManagerProto.CONTROLLER_STATE_DESTROYED;
+ case STATE_UNKNOWN:
+ default:
+ return LocationTimeZoneManagerProto.CONTROLLER_STATE_UNKNOWN;
+ }
+ }
+
private static void writeProviderStates(DualDumpOutputStream outputStream,
List<LocationTimeZoneProvider.ProviderState> providerStates, String fieldName,
long fieldId) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
index 4dff02e..b2d9a3f 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
@@ -16,31 +16,64 @@
package com.android.server.timezonedetector.location;
+import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
+import static android.service.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+
import android.annotation.DurationMillisLong;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
-import android.os.Handler;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.service.timezone.TimeZoneProviderEvent;
+import android.service.timezone.TimeZoneProviderSuggestion;
+import android.util.IndentingPrintWriter;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
-import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
+import com.android.server.timezonedetector.ReferenceWithHistory;
+import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Objects;
/**
- * An base class for the component responsible handling events from {@link
- * LocationTimeZoneProvider}s and synthesizing time zone ID suggestions for sending to the time zone
- * detector. This interface primarily exists to extract testable detection logic, i.e. with
- * a minimal number of threading considerations or dependencies on Android infrastructure.
+ * The component responsible handling events from {@link LocationTimeZoneProvider}s and synthesizing
+ * time zone ID suggestions for sending to the time zone detector.
+ *
+ * <p>This class primarily exists to extract unit-testable logic from the surrounding service class,
+ * i.e. with a minimal number of threading considerations or direct dependencies on Android
+ * infrastructure.
+ *
+ * <p>This class supports a primary and a secondary {@link LocationTimeZoneProvider}. The primary is
+ * used until it fails or becomes uncertain. The secondary will then be started. The controller will
+ * immediately make suggestions based on "certain" {@link TimeZoneProviderEvent}s, i.e. events that
+ * demonstrate the provider is certain what the time zone is. The controller will not make immediate
+ * suggestions based on "uncertain" events, giving providers time to change their mind. This also
+ * gives the secondary provider time to initialize when the primary becomes uncertain.
*
* <p>The controller interacts with the following components:
* <ul>
- * <li>The surrounding service, which calls {@link #initialize(Environment, Callback)} and
- * {@link #onConfigurationInternalChanged()}.</li>
- * <li>The {@link Environment} through which obtains information it needs.</li>
+ * <li>The surrounding service, which calls {@link #initialize(Environment, Callback)}.
+ * <li>The {@link Environment} through which it obtains information it needs.</li>
* <li>The {@link Callback} through which it makes time zone suggestions.</li>
* <li>Any {@link LocationTimeZoneProvider} instances it owns, which communicate via the
* {@link LocationTimeZoneProvider.ProviderListener#onProviderStateChange(ProviderState)}
@@ -49,8 +82,9 @@
*
* <p>All incoming calls except for {@link
* LocationTimeZoneProviderController#dump(android.util.IndentingPrintWriter, String[])} must be
- * made on the {@link Handler} thread of the {@link ThreadingDomain} passed to {@link
- * #LocationTimeZoneProviderController(ThreadingDomain)}.
+ * made on the {@link android.os.Handler} thread of the {@link ThreadingDomain} passed to {@link
+ * #LocationTimeZoneProviderController(ThreadingDomain, LocationTimeZoneProvider,
+ * LocationTimeZoneProvider)}.
*
* <p>Provider / controller integration notes:
*
@@ -59,43 +93,720 @@
* different from the certainty that there are no time zone IDs for the current location. A provider
* can be certain about there being no time zone IDs for a location for good reason, e.g. for
* disputed areas and oceans. Distinguishing uncertainty allows the controller to try other
- * providers (or give up), where as certainty means it should not.
+ * providers (or give up), whereas certainty means it should not.
*
* <p>A provider can fail permanently. A permanent failure will stop the provider until next
* boot.
*/
-abstract class LocationTimeZoneProviderController implements Dumpable {
+class LocationTimeZoneProviderController implements Dumpable {
- @NonNull protected final ThreadingDomain mThreadingDomain;
- @NonNull protected final Object mSharedLock;
+ // String is used for easier logging / interpretation in bug reports Vs int.
+ @StringDef(prefix = "STATE_",
+ value = { STATE_UNKNOWN, STATE_PROVIDERS_INITIALIZING, STATE_STOPPED,
+ STATE_INITIALIZING, STATE_UNCERTAIN, STATE_CERTAIN, STATE_FAILED,
+ STATE_DESTROYED })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
+ @interface State {}
- LocationTimeZoneProviderController(@NonNull ThreadingDomain threadingDomain) {
+ /** The state used for an uninitialized controller. */
+ static final @State String STATE_UNKNOWN = "UNKNOWN";
+
+ /**
+ * A state used while the location time zone providers are initializing. Enables detection
+ * / avoidance of unwanted fail-over behavior before both providers are initialized.
+ */
+ static final @State String STATE_PROVIDERS_INITIALIZING = "PROVIDERS_INITIALIZING";
+ /** An inactive state: Detection is disabled. */
+ static final @State String STATE_STOPPED = "STOPPED";
+ /** An active state: No suggestion has yet been made. */
+ static final @State String STATE_INITIALIZING = "INITIALIZING";
+ /** An active state: The last suggestion was "uncertain". */
+ static final @State String STATE_UNCERTAIN = "UNCERTAIN";
+ /** An active state: The last suggestion was "certain". */
+ static final @State String STATE_CERTAIN = "CERTAIN";
+ /** An inactive state: The location time zone providers have failed. */
+ static final @State String STATE_FAILED = "FAILED";
+ /** An inactive state: The controller is destroyed. */
+ static final @State String STATE_DESTROYED = "DESTROYED";
+
+ @NonNull private final ThreadingDomain mThreadingDomain;
+ @NonNull private final Object mSharedLock;
+ /**
+ * Used for scheduling uncertainty timeouts, i.e. after a provider has reported uncertainty.
+ * This timeout is not provider-specific: it is started when the controller becomes uncertain
+ * due to events it has received from one or other provider.
+ */
+ @NonNull private final SingleRunnableQueue mUncertaintyTimeoutQueue;
+
+ @NonNull private final MetricsLogger mMetricsLogger;
+ @NonNull private final LocationTimeZoneProvider mPrimaryProvider;
+ @NonNull private final LocationTimeZoneProvider mSecondaryProvider;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private ConfigurationInternal mCurrentUserConfiguration;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private Environment mEnvironment;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private Callback mCallback;
+
+ /** Usually {@code false} but can be set to {@code true} to record state changes for testing. */
+ private final boolean mRecordStateChanges;
+
+ @GuardedBy("mSharedLock")
+ @NonNull
+ private final ArrayList<@State String> mRecordedStates = new ArrayList<>(0);
+
+ /**
+ * The current state. This is primarily for metrics / reporting of how long the controller
+ * spends active / inactive during a period. There is overlap with the provider states, but
+ * providers operate independently of each other, so this can help to understand how long the
+ * geo detection system overall was certain or uncertain when multiple providers might have been
+ * enabled concurrently.
+ */
+ @GuardedBy("mSharedLock")
+ private final ReferenceWithHistory<@State String> mState = new ReferenceWithHistory<>(10);
+
+ /** Contains the last suggestion actually made, if there is one. */
+ @GuardedBy("mSharedLock")
+ @Nullable
+ private GeolocationTimeZoneSuggestion mLastSuggestion;
+
+ LocationTimeZoneProviderController(@NonNull ThreadingDomain threadingDomain,
+ @NonNull MetricsLogger metricsLogger,
+ @NonNull LocationTimeZoneProvider primaryProvider,
+ @NonNull LocationTimeZoneProvider secondaryProvider,
+ boolean recordStateChanges) {
mThreadingDomain = Objects.requireNonNull(threadingDomain);
mSharedLock = threadingDomain.getLockObject();
+ mUncertaintyTimeoutQueue = threadingDomain.createSingleRunnableQueue();
+ mMetricsLogger = Objects.requireNonNull(metricsLogger);
+ mPrimaryProvider = Objects.requireNonNull(primaryProvider);
+ mSecondaryProvider = Objects.requireNonNull(secondaryProvider);
+ mRecordStateChanges = recordStateChanges;
+
+ synchronized (mSharedLock) {
+ mState.set(STATE_UNKNOWN);
+ }
}
/**
* Called to initialize the controller during boot. Called once only.
* {@link LocationTimeZoneProvider#initialize} must be called by this method.
*/
- abstract void initialize(@NonNull Environment environment, @NonNull Callback callback);
+ void initialize(@NonNull Environment environment, @NonNull Callback callback) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ debugLog("initialize()");
+ mEnvironment = Objects.requireNonNull(environment);
+ mCallback = Objects.requireNonNull(callback);
+ mCurrentUserConfiguration = environment.getCurrentUserConfigurationInternal();
+
+ LocationTimeZoneProvider.ProviderListener providerListener =
+ LocationTimeZoneProviderController.this::onProviderStateChange;
+ setState(STATE_PROVIDERS_INITIALIZING);
+ mPrimaryProvider.initialize(providerListener);
+ mSecondaryProvider.initialize(providerListener);
+ setState(STATE_STOPPED);
+
+ alterProvidersStartedStateIfRequired(
+ null /* oldConfiguration */, mCurrentUserConfiguration);
+ }
+ }
/**
* Called when the content of the {@link ConfigurationInternal} may have changed. The receiver
* should call {@link Environment#getCurrentUserConfigurationInternal()} to get the current
* user's config. This call must be made on the {@link ThreadingDomain} handler thread.
*/
- abstract void onConfigurationInternalChanged();
+ void onConfigurationInternalChanged() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ debugLog("onConfigChanged()");
+
+ ConfigurationInternal oldConfig = mCurrentUserConfiguration;
+ ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal();
+ mCurrentUserConfiguration = newConfig;
+
+ if (!newConfig.equals(oldConfig)) {
+ if (newConfig.getUserId() != oldConfig.getUserId()) {
+ // If the user changed, stop the providers if needed. They may be re-started
+ // for the new user immediately afterwards if their settings allow.
+ String reason = "User changed. old=" + oldConfig.getUserId()
+ + ", new=" + newConfig.getUserId();
+ debugLog("Stopping providers: " + reason);
+ stopProviders(reason);
+
+ alterProvidersStartedStateIfRequired(null /* oldConfiguration */, newConfig);
+ } else {
+ alterProvidersStartedStateIfRequired(oldConfig, newConfig);
+ }
+ }
+ }
+ }
@VisibleForTesting
- abstract boolean isUncertaintyTimeoutSet();
+ boolean isUncertaintyTimeoutSet() {
+ return mUncertaintyTimeoutQueue.hasQueued();
+ }
@VisibleForTesting
@DurationMillisLong
- abstract long getUncertaintyTimeoutDelayMillis();
+ long getUncertaintyTimeoutDelayMillis() {
+ return mUncertaintyTimeoutQueue.getQueuedDelayMillis();
+ }
/** Called if the geolocation time zone detection is being reconfigured. */
- abstract void destroy();
+ void destroy() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ stopProviders("destroy()");
+
+ // Enter destroyed state.
+ mPrimaryProvider.destroy();
+ mSecondaryProvider.destroy();
+ setState(STATE_DESTROYED);
+ }
+ }
+
+ /**
+ * Updates {@link #mState} if needed, and performs all the record-keeping / callbacks associated
+ * with state changes.
+ */
+ @GuardedBy("mSharedLock")
+ private void setState(@State String state) {
+ if (!Objects.equals(mState.get(), state)) {
+ mState.set(state);
+ if (mRecordStateChanges) {
+ mRecordedStates.add(state);
+ }
+ mMetricsLogger.onStateChange(state);
+ }
+ }
+
+ @GuardedBy("mSharedLock")
+ private void stopProviders(@NonNull String reason) {
+ stopProviderIfStarted(mPrimaryProvider);
+ stopProviderIfStarted(mSecondaryProvider);
+
+ // By definition, if both providers are stopped, the controller is uncertain.
+ cancelUncertaintyTimeout();
+
+ // If a previous "certain" suggestion has been made, then a new "uncertain"
+ // suggestion must now be made to indicate the controller {does not / no longer has}
+ // an opinion and will not be sending further updates (until at least the providers are
+ // re-started).
+ if (Objects.equals(mState.get(), STATE_CERTAIN)) {
+ GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+ mEnvironment.elapsedRealtimeMillis(),
+ "Withdraw previous suggestion, providers are stopping: " + reason);
+ makeSuggestion(suggestion, STATE_UNCERTAIN);
+ }
+ setState(STATE_STOPPED);
+ }
+
+ @GuardedBy("mSharedLock")
+ private void stopProviderIfStarted(@NonNull LocationTimeZoneProvider provider) {
+ if (provider.getCurrentState().isStarted()) {
+ stopProvider(provider);
+ }
+ }
+
+ @GuardedBy("mSharedLock")
+ private void stopProvider(@NonNull LocationTimeZoneProvider provider) {
+ ProviderState providerState = provider.getCurrentState();
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_STOPPED: {
+ debugLog("No need to stop " + provider + ": already stopped");
+ break;
+ }
+ case PROVIDER_STATE_STARTED_INITIALIZING:
+ case PROVIDER_STATE_STARTED_CERTAIN:
+ case PROVIDER_STATE_STARTED_UNCERTAIN: {
+ debugLog("Stopping " + provider);
+ provider.stopUpdates();
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED:
+ case PROVIDER_STATE_DESTROYED: {
+ debugLog("Unable to stop " + provider + ": it is terminated.");
+ break;
+ }
+ default: {
+ warnLog("Unknown provider state: " + provider);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Sets the providers into the correct started/stopped state for the {@code newConfiguration}
+ * and, if there is a provider state change, makes any suggestions required to inform the
+ * downstream time zone detection code.
+ *
+ * <p>This is a utility method that exists to avoid duplicated logic for the various cases when
+ * provider started / stopped state may need to be set or changed, e.g. during initialization
+ * or when a new configuration has been received.
+ */
+ @GuardedBy("mSharedLock")
+ private void alterProvidersStartedStateIfRequired(
+ @Nullable ConfigurationInternal oldConfiguration,
+ @NonNull ConfigurationInternal newConfiguration) {
+
+ // Provider started / stopped states only need to be changed if geoDetectionEnabled has
+ // changed.
+ boolean oldGeoDetectionEnabled = oldConfiguration != null
+ && oldConfiguration.getGeoDetectionEnabledBehavior();
+ boolean newGeoDetectionEnabled = newConfiguration.getGeoDetectionEnabledBehavior();
+ if (oldGeoDetectionEnabled == newGeoDetectionEnabled) {
+ return;
+ }
+
+ // The check above ensures that the logic below only executes if providers are going from
+ // {started *} -> {stopped}, or {stopped} -> {started initializing}. If this changes in
+ // future and there could be {started *} -> {started *} cases, or cases where the provider
+ // can't be assumed to go straight to the {started initializing} state, then the logic below
+ // would need to cover extra conditions, for example:
+ // 1) If the primary is in {started uncertain}, the secondary should be started.
+ // 2) If (1), and the secondary instantly enters the {perm failed} state, the uncertainty
+ // timeout started when the primary entered {started uncertain} should be cancelled.
+
+ if (newGeoDetectionEnabled) {
+ setState(STATE_INITIALIZING);
+
+ // Try to start the primary provider.
+ tryStartProvider(mPrimaryProvider, newConfiguration);
+
+ // The secondary should only ever be started if the primary now isn't started (i.e. it
+ // couldn't become {started initializing} because it is {perm failed}).
+ ProviderState newPrimaryState = mPrimaryProvider.getCurrentState();
+ if (!newPrimaryState.isStarted()) {
+ // If the primary provider is {perm failed} then the controller must try to start
+ // the secondary.
+ tryStartProvider(mSecondaryProvider, newConfiguration);
+
+ ProviderState newSecondaryState = mSecondaryProvider.getCurrentState();
+ if (!newSecondaryState.isStarted()) {
+ // If both providers are {perm failed} then the controller immediately
+ // reports uncertain.
+ GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+ mEnvironment.elapsedRealtimeMillis(),
+ "Providers are failed:"
+ + " primary=" + mPrimaryProvider.getCurrentState()
+ + " secondary=" + mPrimaryProvider.getCurrentState());
+ makeSuggestion(suggestion, STATE_FAILED);
+ }
+ }
+ } else {
+ stopProviders("Geo detection behavior disabled");
+ }
+ }
+
+ @GuardedBy("mSharedLock")
+ private void tryStartProvider(@NonNull LocationTimeZoneProvider provider,
+ @NonNull ConfigurationInternal configuration) {
+ ProviderState providerState = provider.getCurrentState();
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_STOPPED: {
+ debugLog("Enabling " + provider);
+ provider.startUpdates(configuration,
+ mEnvironment.getProviderInitializationTimeout(),
+ mEnvironment.getProviderInitializationTimeoutFuzz(),
+ mEnvironment.getProviderEventFilteringAgeThreshold());
+ break;
+ }
+ case PROVIDER_STATE_STARTED_INITIALIZING:
+ case PROVIDER_STATE_STARTED_CERTAIN:
+ case PROVIDER_STATE_STARTED_UNCERTAIN: {
+ debugLog("No need to start " + provider + ": already started");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED:
+ case PROVIDER_STATE_DESTROYED: {
+ debugLog("Unable to start " + provider + ": it is terminated");
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unknown provider state:"
+ + " provider=" + provider);
+ }
+ }
+ }
+
+ void onProviderStateChange(@NonNull ProviderState providerState) {
+ mThreadingDomain.assertCurrentThread();
+ LocationTimeZoneProvider provider = providerState.provider;
+ assertProviderKnown(provider);
+
+ synchronized (mSharedLock) {
+ // Ignore provider state changes during initialization. e.g. if the primary provider
+ // moves to PROVIDER_STATE_PERM_FAILED during initialization, the secondary will not
+ // be ready to take over yet.
+ if (Objects.equals(mState.get(), STATE_PROVIDERS_INITIALIZING)) {
+ warnLog("onProviderStateChange: Ignoring provider state change because both"
+ + " providers have not yet completed initialization."
+ + " providerState=" + providerState);
+ return;
+ }
+
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_STARTED_INITIALIZING:
+ case PROVIDER_STATE_STOPPED:
+ case PROVIDER_STATE_DESTROYED: {
+ // This should never happen: entering initializing, stopped or destroyed are
+ // triggered by the controller so and should not trigger a state change
+ // callback.
+ warnLog("onProviderStateChange: Unexpected state change for provider,"
+ + " provider=" + provider);
+ break;
+ }
+ case PROVIDER_STATE_STARTED_CERTAIN:
+ case PROVIDER_STATE_STARTED_UNCERTAIN: {
+ // These are valid and only happen if an event is received while the provider is
+ // started.
+ debugLog("onProviderStateChange: Received notification of a state change while"
+ + " started, provider=" + provider);
+ handleProviderStartedStateChange(providerState);
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("Received notification of permanent failure for"
+ + " provider=" + provider);
+ handleProviderFailedStateChange(providerState);
+ break;
+ }
+ default: {
+ warnLog("onProviderStateChange: Unexpected provider=" + provider);
+ }
+ }
+ }
+ }
+
+ private void assertProviderKnown(@NonNull LocationTimeZoneProvider provider) {
+ if (provider != mPrimaryProvider && provider != mSecondaryProvider) {
+ throw new IllegalArgumentException("Unknown provider: " + provider);
+ }
+ }
+
+ /**
+ * Called when a provider has reported that it has failed permanently.
+ */
+ @GuardedBy("mSharedLock")
+ private void handleProviderFailedStateChange(@NonNull ProviderState providerState) {
+ LocationTimeZoneProvider failedProvider = providerState.provider;
+ ProviderState primaryCurrentState = mPrimaryProvider.getCurrentState();
+ ProviderState secondaryCurrentState = mSecondaryProvider.getCurrentState();
+
+ // If a provider has failed, the other may need to be started.
+ if (failedProvider == mPrimaryProvider) {
+ if (!secondaryCurrentState.isTerminated()) {
+ // Try to start the secondary. This does nothing if the provider is already
+ // started, and will leave the provider in {started initializing} if the provider is
+ // stopped.
+ tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration);
+ }
+ } else if (failedProvider == mSecondaryProvider) {
+ // No-op: The secondary will only be active if the primary is uncertain or is
+ // terminated. So, there the primary should not need to be started when the secondary
+ // fails.
+ if (primaryCurrentState.stateEnum != PROVIDER_STATE_STARTED_UNCERTAIN
+ && !primaryCurrentState.isTerminated()) {
+ warnLog("Secondary provider unexpected reported a failure:"
+ + " failed provider=" + failedProvider.getName()
+ + ", primary provider=" + mPrimaryProvider
+ + ", secondary provider=" + mSecondaryProvider);
+ }
+ }
+
+ // If both providers are now terminated, the controller needs to tell the next component in
+ // the time zone detection process.
+ if (primaryCurrentState.isTerminated() && secondaryCurrentState.isTerminated()) {
+
+ // If both providers are newly terminated then the controller is uncertain by definition
+ // and it will never recover so it can send a suggestion immediately.
+ cancelUncertaintyTimeout();
+
+ // If both providers are now terminated, then a suggestion must be sent informing the
+ // time zone detector that there are no further updates coming in the future.
+ GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+ mEnvironment.elapsedRealtimeMillis(),
+ "Both providers are terminated:"
+ + " primary=" + primaryCurrentState.provider
+ + ", secondary=" + secondaryCurrentState.provider);
+ makeSuggestion(suggestion, STATE_FAILED);
+ }
+ }
+
+ /**
+ * Called when a provider has changed state but just moved from one started state to another
+ * started state, usually as a result of a new {@link TimeZoneProviderEvent} being received.
+ * However, there are rare cases where the event can also be null.
+ */
+ @GuardedBy("mSharedLock")
+ private void handleProviderStartedStateChange(@NonNull ProviderState providerState) {
+ LocationTimeZoneProvider provider = providerState.provider;
+ TimeZoneProviderEvent event = providerState.event;
+ if (event == null) {
+ // Implicit uncertainty, i.e. where the provider is started, but a problem has been
+ // detected without having received an event. For example, if the process has detected
+ // the loss of a binder-based provider, or initialization took too long. This is treated
+ // the same as explicit uncertainty, i.e. where the provider has explicitly told this
+ // process it is uncertain.
+ long uncertaintyStartedElapsedMillis = mEnvironment.elapsedRealtimeMillis();
+ handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis,
+ "provider=" + provider + ", implicit uncertainty, event=null");
+ return;
+ }
+
+ if (!mCurrentUserConfiguration.getGeoDetectionEnabledBehavior()) {
+ // This should not happen: the provider should not be in an started state if the user
+ // does not have geodetection enabled.
+ warnLog("Provider=" + provider + " is started, but"
+ + " currentUserConfiguration=" + mCurrentUserConfiguration
+ + " suggests it shouldn't be.");
+ }
+
+ switch (event.getType()) {
+ case EVENT_TYPE_PERMANENT_FAILURE: {
+ // This shouldn't happen. A provider cannot be started and have this event type.
+ warnLog("Provider=" + provider + " is started, but event suggests it shouldn't be");
+ break;
+ }
+ case EVENT_TYPE_UNCERTAIN: {
+ long uncertaintyStartedElapsedMillis = event.getCreationElapsedMillis();
+ handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis,
+ "provider=" + provider + ", explicit uncertainty. event=" + event);
+ break;
+ }
+ case EVENT_TYPE_SUGGESTION: {
+ handleProviderSuggestion(provider, event);
+ break;
+ }
+ default: {
+ warnLog("Unknown eventType=" + event.getType());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Called when a provider has become "certain" about the time zone(s).
+ */
+ @GuardedBy("mSharedLock")
+ private void handleProviderSuggestion(
+ @NonNull LocationTimeZoneProvider provider,
+ @NonNull TimeZoneProviderEvent providerEvent) {
+
+ // By definition, the controller is now certain.
+ cancelUncertaintyTimeout();
+
+ if (provider == mPrimaryProvider) {
+ stopProviderIfStarted(mSecondaryProvider);
+ }
+
+ TimeZoneProviderSuggestion providerSuggestion = providerEvent.getSuggestion();
+
+ // For the suggestion's effectiveFromElapsedMillis, use the time embedded in the provider's
+ // suggestion (which indicates the time when the provider detected the location used to
+ // establish the time zone).
+ //
+ // An alternative would be to use the current time or the providerEvent creation time, but
+ // this would hinder the ability for the time_zone_detector to judge which suggestions are
+ // based on newer information when comparing suggestions between different sources.
+ long effectiveFromElapsedMillis = providerSuggestion.getElapsedRealtimeMillis();
+ GeolocationTimeZoneSuggestion geoSuggestion =
+ GeolocationTimeZoneSuggestion.createCertainSuggestion(
+ effectiveFromElapsedMillis, providerSuggestion.getTimeZoneIds());
+
+ String debugInfo = "Event received provider=" + provider
+ + ", providerEvent=" + providerEvent
+ + ", suggestionCreationTime=" + mEnvironment.elapsedRealtimeMillis();
+ geoSuggestion.addDebugInfo(debugInfo);
+ makeSuggestion(geoSuggestion, STATE_CERTAIN);
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("LocationTimeZoneProviderController:");
+
+ ipw.increaseIndent(); // level 1
+ ipw.println("mCurrentUserConfiguration=" + mCurrentUserConfiguration);
+ ipw.println("providerInitializationTimeout="
+ + mEnvironment.getProviderInitializationTimeout());
+ ipw.println("providerInitializationTimeoutFuzz="
+ + mEnvironment.getProviderInitializationTimeoutFuzz());
+ ipw.println("uncertaintyDelay=" + mEnvironment.getUncertaintyDelay());
+ ipw.println("mState=" + mState.get());
+ ipw.println("mLastSuggestion=" + mLastSuggestion);
+
+ ipw.println("State history:");
+ ipw.increaseIndent(); // level 2
+ mState.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+
+ ipw.println("Primary Provider:");
+ ipw.increaseIndent(); // level 2
+ mPrimaryProvider.dump(ipw, args);
+ ipw.decreaseIndent(); // level 2
+
+ ipw.println("Secondary Provider:");
+ ipw.increaseIndent(); // level 2
+ mSecondaryProvider.dump(ipw, args);
+ ipw.decreaseIndent(); // level 2
+
+ ipw.decreaseIndent(); // level 1
+ }
+ }
+
+ /**
+ * Sends an immediate suggestion and enters a new state if needed. This method updates
+ * mLastSuggestion and changes mStateEnum / reports the new state for metrics.
+ */
+ @GuardedBy("mSharedLock")
+ private void makeSuggestion(@NonNull GeolocationTimeZoneSuggestion suggestion,
+ @State String newState) {
+ debugLog("makeSuggestion: suggestion=" + suggestion);
+ mCallback.suggest(suggestion);
+ mLastSuggestion = suggestion;
+ setState(newState);
+ }
+
+ /** Clears the uncertainty timeout. */
+ @GuardedBy("mSharedLock")
+ private void cancelUncertaintyTimeout() {
+ mUncertaintyTimeoutQueue.cancel();
+ }
+
+ /**
+ * Called when a provider has become "uncertain" about the time zone.
+ *
+ * <p>A provider is expected to report its uncertainty as soon as it becomes uncertain, as
+ * this enables the most flexibility for the controller to start other providers when there are
+ * multiple ones available. The controller is therefore responsible for deciding when to make a
+ * "uncertain" suggestion to the downstream time zone detector.
+ *
+ * <p>This method schedules an "uncertainty" timeout (if one isn't already scheduled) to be
+ * triggered later if nothing else preempts it. It can be preempted if the provider becomes
+ * certain (or does anything else that calls {@link
+ * #makeSuggestion(GeolocationTimeZoneSuggestion, String)}) within {@link
+ * Environment#getUncertaintyDelay()}. Preemption causes the scheduled
+ * "uncertainty" timeout to be cancelled. If the provider repeatedly sends uncertainty events
+ * within the uncertainty delay period, those events are effectively ignored (i.e. the timeout
+ * is not reset each time).
+ */
+ @GuardedBy("mSharedLock")
+ void handleProviderUncertainty(
+ @NonNull LocationTimeZoneProvider provider,
+ @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis,
+ @NonNull String reason) {
+ Objects.requireNonNull(provider);
+
+ // Start the uncertainty timeout if needed to ensure the controller will eventually make an
+ // uncertain suggestion if no success event arrives in time to counteract it.
+ if (!mUncertaintyTimeoutQueue.hasQueued()) {
+ debugLog("Starting uncertainty timeout: reason=" + reason);
+
+ Duration uncertaintyDelay = mEnvironment.getUncertaintyDelay();
+ mUncertaintyTimeoutQueue.runDelayed(
+ () -> onProviderUncertaintyTimeout(
+ provider, uncertaintyStartedElapsedMillis, uncertaintyDelay),
+ uncertaintyDelay.toMillis());
+ }
+
+ if (provider == mPrimaryProvider) {
+ // (Try to) start the secondary. It could already be started, or enabling might not
+ // succeed if the provider has previously reported it is perm failed. The uncertainty
+ // timeout (set above) is used to ensure that an uncertain suggestion will be made if
+ // the secondary cannot generate a success event in time.
+ tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration);
+ }
+ }
+
+ private void onProviderUncertaintyTimeout(
+ @NonNull LocationTimeZoneProvider provider,
+ @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis,
+ @NonNull Duration uncertaintyDelay) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ long afterUncertaintyTimeoutElapsedMillis = mEnvironment.elapsedRealtimeMillis();
+
+ // For the effectiveFromElapsedMillis suggestion property, use the
+ // uncertaintyStartedElapsedMillis. This is the time when the provider first reported
+ // uncertainty, i.e. before the uncertainty timeout.
+ //
+ // afterUncertaintyTimeoutElapsedMillis could be used instead, which is the time when
+ // the location_time_zone_manager finally confirms that the time zone was uncertain,
+ // but the suggestion property allows the information to be back-dated, which should
+ // help when comparing suggestions from different sources.
+ GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+ uncertaintyStartedElapsedMillis,
+ "Uncertainty timeout triggered for " + provider.getName() + ":"
+ + " primary=" + mPrimaryProvider
+ + ", secondary=" + mSecondaryProvider
+ + ", uncertaintyStarted="
+ + Duration.ofMillis(uncertaintyStartedElapsedMillis)
+ + ", afterUncertaintyTimeout="
+ + Duration.ofMillis(afterUncertaintyTimeoutElapsedMillis)
+ + ", uncertaintyDelay=" + uncertaintyDelay
+ );
+ makeSuggestion(suggestion, STATE_UNCERTAIN);
+ }
+ }
+
+ @NonNull
+ private static GeolocationTimeZoneSuggestion createUncertainSuggestion(
+ @ElapsedRealtimeLong long effectiveFromElapsedMillis,
+ @NonNull String reason) {
+ GeolocationTimeZoneSuggestion suggestion =
+ GeolocationTimeZoneSuggestion.createUncertainSuggestion(
+ effectiveFromElapsedMillis);
+ suggestion.addDebugInfo(reason);
+ return suggestion;
+ }
+
+ /**
+ * Clears recorded controller and provider state changes (for use during tests).
+ */
+ void clearRecordedStates() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ mRecordedStates.clear();
+ mPrimaryProvider.clearRecordedStates();
+ mSecondaryProvider.clearRecordedStates();
+ }
+ }
+
+ /**
+ * Returns a snapshot of the current controller state for tests.
+ */
+ @NonNull
+ LocationTimeZoneManagerServiceState getStateForTests() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ LocationTimeZoneManagerServiceState.Builder builder =
+ new LocationTimeZoneManagerServiceState.Builder();
+ if (mLastSuggestion != null) {
+ builder.setLastSuggestion(mLastSuggestion);
+ }
+ builder.setControllerState(mState.get())
+ .setStateChanges(mRecordedStates)
+ .setPrimaryProviderStateChanges(mPrimaryProvider.getRecordedStates())
+ .setSecondaryProviderStateChanges(mSecondaryProvider.getRecordedStates());
+ return builder.build();
+ }
+ }
/**
* Used by {@link LocationTimeZoneProviderController} to obtain information from the surrounding
@@ -167,4 +878,12 @@
*/
abstract void suggest(@NonNull GeolocationTimeZoneSuggestion suggestion);
}
+
+ /**
+ * Used by {@link LocationTimeZoneProviderController} to record events for metrics / telemetry.
+ */
+ interface MetricsLogger {
+ /** Called when the controller's state changes. */
+ void onStateChange(@State String stateEnum);
+ }
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java
similarity index 82%
rename from services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java
index 46eaad0..0c751aa 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerCallbackImpl.java
@@ -24,11 +24,12 @@
/**
* The real implementation of {@link LocationTimeZoneProviderController.Callback} used by
- * {@link ControllerImpl} to interact with other server components.
+ * {@link LocationTimeZoneProviderController} to interact with other server components.
*/
-class ControllerCallbackImpl extends LocationTimeZoneProviderController.Callback {
+class LocationTimeZoneProviderControllerCallbackImpl
+ extends LocationTimeZoneProviderController.Callback {
- ControllerCallbackImpl(@NonNull ThreadingDomain threadingDomain) {
+ LocationTimeZoneProviderControllerCallbackImpl(@NonNull ThreadingDomain threadingDomain) {
super(threadingDomain);
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerEnvironmentImpl.java
similarity index 90%
rename from services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerEnvironmentImpl.java
index 33cdc5f..e7d16c8 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerEnvironmentImpl.java
@@ -29,14 +29,15 @@
/**
* The real implementation of {@link LocationTimeZoneProviderController.Environment} used by
- * {@link ControllerImpl} to interact with other server components.
+ * {@link LocationTimeZoneProviderController} to interact with other server components.
*/
-class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
+class LocationTimeZoneProviderControllerEnvironmentImpl
+ extends LocationTimeZoneProviderController.Environment {
@NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
@NonNull private final ConfigurationChangeListener mConfigurationInternalChangeListener;
- ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
+ LocationTimeZoneProviderControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
@NonNull ServiceConfigAccessor serviceConfigAccessor,
@NonNull LocationTimeZoneProviderController controller) {
super(threadingDomain);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e9d5ad6..59b6a08 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -470,14 +470,16 @@
}
private void stopUser(int userId) {
- if (userId == mCurrentUserId) {
- switchUser(ActivityManager.getCurrentUser());
- return;
- }
+ synchronized (mLock) {
+ if (userId == mCurrentUserId) {
+ switchUser(ActivityManager.getCurrentUser());
+ return;
+ }
- releaseSessionOfUserLocked(userId);
- unbindServiceOfUserLocked(userId);
- mRunningProfiles.remove(userId);
+ releaseSessionOfUserLocked(userId);
+ unbindServiceOfUserLocked(userId);
+ mRunningProfiles.remove(userId);
+ }
}
private void startProfileLocked(int userId) {
@@ -3010,32 +3012,47 @@
public void addHardwareInput(int deviceId, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
- synchronized (mLock) {
- mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
- addHardwareInputLocked(inputInfo);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
+ addHardwareInputLocked(inputInfo);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
public void addHdmiInput(int id, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
- synchronized (mLock) {
- mTvInputHardwareManager.addHdmiInput(id, inputInfo);
- addHardwareInputLocked(inputInfo);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mTvInputHardwareManager.addHdmiInput(id, inputInfo);
+ addHardwareInputLocked(inputInfo);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
public void removeHardwareInput(String inputId) {
ensureHardwarePermission();
- synchronized (mLock) {
- ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
- boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
- if (removed) {
- buildTvInputListLocked(mUserId, null);
- mTvInputHardwareManager.removeHardwareInput(inputId);
- } else {
- Slog.e(TAG, "failed to remove input " + inputId);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
+ boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
+ if (removed) {
+ buildTvInputListLocked(mUserId, null);
+ mTvInputHardwareManager.removeHardwareInput(inputId);
+ } else {
+ Slog.e(TAG, "failed to remove input " + inputId);
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
}
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 4d1ff9e..c7b6421 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -27,6 +27,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.BroadcastInfoResponse;
import android.media.tv.interactive.ITvIAppClient;
import android.media.tv.interactive.ITvIAppManager;
import android.media.tv.interactive.ITvIAppManagerCallback;
@@ -42,9 +45,11 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.InputChannel;
import android.view.Surface;
import com.android.internal.annotations.GuardedBy;
@@ -58,10 +63,12 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
/**
* This class provides a system service that manages interactive TV applications.
*/
@@ -81,6 +88,8 @@
@GuardedBy("mLock")
private final SparseArray<UserState> mUserStates = new SparseArray<>();
+ private final UserManager mUserManager;
+
/**
* Initializes the system service.
* <p>
@@ -93,6 +102,7 @@
public TvIAppManagerService(Context context) {
super(context);
mContext = context;
+ mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
}
@GuardedBy("mLock")
@@ -330,11 +340,188 @@
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // TODO: handle switch / start / stop user
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ } else if (Intent.ACTION_USER_STARTED.equals(action)) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ startUser(userId);
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ stopUser(userId);
+ }
}
}, UserHandle.ALL, intentFilter, null, null);
}
+ private void switchUser(int userId) {
+ synchronized (mLock) {
+ if (mCurrentUserId == userId) {
+ return;
+ }
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (userInfo.isProfile()) {
+ Slog.w(TAG, "cannot switch to a profile!");
+ return;
+ }
+
+ for (int runningId : mRunningProfiles) {
+ releaseSessionOfUserLocked(runningId);
+ unbindServiceOfUserLocked(runningId);
+ }
+ mRunningProfiles.clear();
+ releaseSessionOfUserLocked(mCurrentUserId);
+ unbindServiceOfUserLocked(mCurrentUserId);
+
+ mCurrentUserId = userId;
+ buildTvIAppServiceListLocked(userId, null);
+ }
+ }
+
+ private void removeUser(int userId) {
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(userId);
+ if (userState == null) {
+ return;
+ }
+ // Release all created sessions.
+ for (SessionState state : userState.mSessionStateMap.values()) {
+ if (state.mSession != null) {
+ try {
+ state.mSession.release();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in release", e);
+ }
+ }
+ }
+ userState.mSessionStateMap.clear();
+
+ // Unregister all callbacks and unbind all services.
+ for (ServiceState serviceState : userState.mServiceStateMap.values()) {
+ if (serviceState.mService != null) {
+ if (serviceState.mCallback != null) {
+ try {
+ serviceState.mService.unregisterCallback(serviceState.mCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
+ }
+ mContext.unbindService(serviceState.mConnection);
+ }
+ }
+ userState.mServiceStateMap.clear();
+
+ // Clear everything else.
+ userState.mIAppMap.clear();
+ userState.mPackageSet.clear();
+ userState.mClientStateMap.clear();
+ userState.mCallbacks.kill();
+
+ mRunningProfiles.remove(userId);
+ mUserStates.remove(userId);
+
+ if (userId == mCurrentUserId) {
+ switchUser(UserHandle.USER_SYSTEM);
+ }
+ }
+ }
+
+ private void startUser(int userId) {
+ synchronized (mLock) {
+ if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
+ // user already started
+ return;
+ }
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ UserInfo parentInfo = mUserManager.getProfileParent(userId);
+ if (userInfo.isProfile()
+ && parentInfo != null
+ && parentInfo.id == mCurrentUserId) {
+ // only the children of the current user can be started in background
+ startProfileLocked(userId);
+ }
+ }
+ }
+
+ private void stopUser(int userId) {
+ synchronized (mLock) {
+ if (userId == mCurrentUserId) {
+ switchUser(ActivityManager.getCurrentUser());
+ return;
+ }
+
+ releaseSessionOfUserLocked(userId);
+ unbindServiceOfUserLocked(userId);
+ mRunningProfiles.remove(userId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void startProfileLocked(int userId) {
+ mRunningProfiles.add(userId);
+ buildTvIAppServiceListLocked(userId, null);
+ }
+
+ @GuardedBy("mLock")
+ private void releaseSessionOfUserLocked(int userId) {
+ UserState userState = getUserStateLocked(userId);
+ if (userState == null) {
+ return;
+ }
+ List<SessionState> sessionStatesToRelease = new ArrayList<>();
+ for (SessionState sessionState : userState.mSessionStateMap.values()) {
+ if (sessionState.mSession != null) {
+ sessionStatesToRelease.add(sessionState);
+ }
+ }
+ for (SessionState sessionState : sessionStatesToRelease) {
+ try {
+ sessionState.mSession.release();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in release", e);
+ }
+ clearSessionAndNotifyClientLocked(sessionState);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void unbindServiceOfUserLocked(int userId) {
+ UserState userState = getUserStateLocked(userId);
+ if (userState == null) {
+ return;
+ }
+ for (Iterator<ComponentName> it = userState.mServiceStateMap.keySet().iterator();
+ it.hasNext(); ) {
+ ComponentName component = it.next();
+ ServiceState serviceState = userState.mServiceStateMap.get(component);
+ if (serviceState != null && serviceState.mSessionTokens.isEmpty()) {
+ if (serviceState.mCallback != null) {
+ try {
+ serviceState.mService.unregisterCallback(serviceState.mCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
+ }
+ mContext.unbindService(serviceState.mConnection);
+ it.remove();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void clearSessionAndNotifyClientLocked(SessionState state) {
+ if (state.mClient != null) {
+ try {
+ state.mClient.onSessionReleased(state.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSessionReleased", e);
+ }
+ }
+ removeSessionStateLocked(state.mSessionToken, state.mUserId);
+ }
+
private SessionState getSessionState(IBinder sessionToken) {
// TODO: implement user state and get session from it.
return null;
@@ -427,14 +614,14 @@
if (userId != mCurrentUserId && !mRunningProfiles.contains(userId)) {
// Only current user and its running profiles can create sessions.
// Let the client get onConnectionFailed callback for this case.
- sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
+ sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
return;
}
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
if (iAppState == null) {
Slogf.w(TAG, "Failed to find state for iAppServiceId=" + iAppServiceId);
- sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
+ sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
return;
}
ServiceState serviceState =
@@ -447,7 +634,7 @@
}
// Send a null token immediately while reconnecting.
if (serviceState.mReconnecting) {
- sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
+ sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
return;
}
@@ -560,6 +747,29 @@
}
@Override
+ public void notifyBroadcastInfoResponse(IBinder sessionToken,
+ BroadcastInfoResponse response, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+ "notifyBroadcastInfoResponse");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).notifyBroadcastInfoResponse(response);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyBroadcastInfoResponse", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void registerCallback(final ITvIAppManagerCallback callback, int userId) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
@@ -596,9 +806,9 @@
@GuardedBy("mLock")
private void sendSessionTokenToClientLocked(ITvIAppClient client, String iAppServiceId,
- IBinder sessionToken, int seq) {
+ IBinder sessionToken, InputChannel channel, int seq) {
try {
- client.onSessionCreated(iAppServiceId, sessionToken, seq);
+ client.onSessionCreated(iAppServiceId, sessionToken, channel, seq);
} catch (RemoteException e) {
Slogf.e(TAG, "error in onSessionCreated", e);
}
@@ -613,20 +823,23 @@
Slogf.d(TAG, "createSessionInternalLocked(iAppServiceId="
+ sessionState.mIAppServiceId + ")");
}
+ InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
// Set up a callback to send the session token.
- ITvIAppSessionCallback callback = new SessionCallback(sessionState);
+ ITvIAppSessionCallback callback = new SessionCallback(sessionState, channels);
boolean created = true;
// Create a session. When failed, send a null token immediately.
try {
- service.createSession(callback, sessionState.mIAppServiceId, sessionState.mType);
+ service.createSession(
+ channels[1], callback, sessionState.mIAppServiceId, sessionState.mType);
} catch (RemoteException e) {
Slogf.e(TAG, "error in createSession", e);
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mIAppServiceId, null,
- sessionState.mSeq);
+ null, sessionState.mSeq);
created = false;
}
+ channels[1].dispose();
return created;
}
@@ -699,7 +912,7 @@
for (SessionState sessionState : sessionsToAbort) {
removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mIAppServiceId, null, sessionState.mSeq);
+ sessionState.mIAppServiceId, null, null, sessionState.mSeq);
}
updateServiceConnectionLocked(serviceState.mComponent, userId);
}
@@ -952,9 +1165,11 @@
private final class SessionCallback extends ITvIAppSessionCallback.Stub {
private final SessionState mSessionState;
+ private final InputChannel[] mInputChannels;
- SessionCallback(SessionState sessionState) {
+ SessionCallback(SessionState sessionState, InputChannel[] channels) {
mSessionState = sessionState;
+ mInputChannels = channels;
}
@Override
@@ -970,12 +1185,14 @@
mSessionState.mClient,
mSessionState.mIAppServiceId,
mSessionState.mSessionToken,
+ mInputChannels[0],
mSessionState.mSeq);
} else {
removeSessionStateLocked(mSessionState.mSessionToken, mSessionState.mUserId);
sendSessionTokenToClientLocked(mSessionState.mClient,
- mSessionState.mIAppServiceId, null, mSessionState.mSeq);
+ mSessionState.mIAppServiceId, null, null, mSessionState.mSeq);
}
+ mInputChannels[0].dispose();
}
}
@@ -998,6 +1215,24 @@
}
}
+ @Override
+ public void onBroadcastInfoRequest(BroadcastInfoRequest request) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onBroadcastInfoRequest (requestId="
+ + request.getRequestId() + ")");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onBroadcastInfoRequest(request, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onBroadcastInfoRequest", e);
+ }
+ }
+ }
+
@GuardedBy("mLock")
private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) {
try {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 1c46ac8..be13168 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -87,9 +87,10 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import com.android.server.vcn.util.LogUtils;
import com.android.server.vcn.util.MtuUtils;
import com.android.server.vcn.util.OneWayBoolean;
@@ -201,7 +202,7 @@
private interface EventInfo {}
/**
- * Sent when there are changes to the underlying network (per the UnderlyingNetworkTracker).
+ * Sent when there are changes to the underlying network (per the UnderlyingNetworkController).
*
* <p>May indicate an entirely new underlying network, OR a change in network properties.
*
@@ -522,11 +523,14 @@
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
- @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+ @NonNull private final UnderlyingNetworkController mUnderlyingNetworkController;
@NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
@NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
- @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
+
+ @NonNull
+ private final VcnUnderlyingNetworkControllerCallback mUnderlyingNetworkControllerCallback;
+
private final boolean mIsMobileDataEnabled;
@NonNull private final IpSecManager mIpSecManager;
@@ -674,17 +678,17 @@
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
- mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
+ mUnderlyingNetworkControllerCallback = new VcnUnderlyingNetworkControllerCallback();
mWakeLock =
mDeps.newWakeLock(mVcnContext.getContext(), PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mUnderlyingNetworkTracker =
- mDeps.newUnderlyingNetworkTracker(
+ mUnderlyingNetworkController =
+ mDeps.newUnderlyingNetworkController(
mVcnContext,
subscriptionGroup,
mLastSnapshot,
- mUnderlyingNetworkTrackerCallback);
+ mUnderlyingNetworkControllerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
addState(mDisconnectedState);
@@ -748,7 +752,7 @@
cancelRetryTimeoutAlarm();
cancelSafeModeAlarm();
- mUnderlyingNetworkTracker.teardown();
+ mUnderlyingNetworkController.teardown();
mGatewayStatusCallback.onQuit();
}
@@ -764,12 +768,13 @@
mVcnContext.ensureRunningOnLooperThread();
mLastSnapshot = snapshot;
- mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
+ mUnderlyingNetworkController.updateSubscriptionSnapshot(mLastSnapshot);
sendMessageAndAcquireWakeLock(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
- private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
+ private class VcnUnderlyingNetworkControllerCallback
+ implements UnderlyingNetworkControllerCallback {
@Override
public void onSelectedUnderlyingNetworkChanged(
@Nullable UnderlyingNetworkRecord underlying) {
@@ -2264,7 +2269,7 @@
+ (mNetworkAgent == null ? null : mNetworkAgent.getNetwork()));
pw.println();
- mUnderlyingNetworkTracker.dump(pw);
+ mUnderlyingNetworkController.dump(pw);
pw.println();
pw.decreaseIndent();
@@ -2276,8 +2281,8 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() {
- return mUnderlyingNetworkTrackerCallback;
+ UnderlyingNetworkControllerCallback getUnderlyingNetworkControllerCallback() {
+ return mUnderlyingNetworkControllerCallback;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -2356,17 +2361,14 @@
/** External dependencies used by VcnGatewayConnection, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
- /** Builds a new UnderlyingNetworkTracker. */
- public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
+ /** Builds a new UnderlyingNetworkController. */
+ public UnderlyingNetworkController newUnderlyingNetworkController(
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkTrackerCallback callback) {
- return new UnderlyingNetworkTracker(
- vcnContext,
- subscriptionGroup,
- snapshot,
- callback);
+ UnderlyingNetworkControllerCallback callback) {
+ return new UnderlyingNetworkController(
+ vcnContext, subscriptionGroup, snapshot, callback);
}
/** Builds a new IkeSession. */
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
new file mode 100644
index 0000000..bea8ae9
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -0,0 +1,196 @@
+/*
+ * 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.server.vcn.routeselection;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import static com.android.server.VcnManagementService.LOCAL_LOG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnManager;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.telephony.SubscriptionManager;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+
+import java.util.Set;
+
+/** @hide */
+class NetworkPriorityClassifier {
+ @NonNull private static final String TAG = NetworkPriorityClassifier.class.getSimpleName();
+ /**
+ * Minimum signal strength for a WiFi network to be eligible for switching to
+ *
+ * <p>A network that satisfies this is eligible to become the selected underlying network with
+ * no additional conditions
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70;
+ /**
+ * Minimum signal strength to continue using a WiFi network
+ *
+ * <p>A network that satisfies the conditions may ONLY continue to be used if it is already
+ * selected as the underlying network. A WiFi network satisfying this condition, but NOT the
+ * prospective-network RSSI threshold CANNOT be switched to.
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
+ /** Priority for any cellular network for which the subscription is listed as opportunistic */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0;
+ /** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_WIFI_IN_USE = 1;
+ /** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_WIFI_PROSPECTIVE = 2;
+ /** Priority for any standard macro cellular network */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_MACRO_CELLULAR = 3;
+ /** Priority for any other networks (including unvalidated, etc) */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_ANY = Integer.MAX_VALUE;
+
+ private static final SparseArray<String> PRIORITY_TO_STRING_MAP = new SparseArray<>();
+
+ static {
+ PRIORITY_TO_STRING_MAP.put(
+ PRIORITY_OPPORTUNISTIC_CELLULAR, "PRIORITY_OPPORTUNISTIC_CELLULAR");
+ PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_IN_USE, "PRIORITY_WIFI_IN_USE");
+ PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_PROSPECTIVE, "PRIORITY_WIFI_PROSPECTIVE");
+ PRIORITY_TO_STRING_MAP.put(PRIORITY_MACRO_CELLULAR, "PRIORITY_MACRO_CELLULAR");
+ PRIORITY_TO_STRING_MAP.put(PRIORITY_ANY, "PRIORITY_ANY");
+ }
+
+ /**
+ * Gives networks a priority class, based on the following priorities:
+ *
+ * <ol>
+ * <li>Opportunistic cellular
+ * <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT
+ * <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT
+ * <li>Macro cellular
+ * <li>Any others
+ * </ol>
+ */
+ static int calculatePriorityClass(
+ UnderlyingNetworkRecord networkRecord,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ final NetworkCapabilities caps = networkRecord.networkCapabilities;
+
+ // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
+
+ if (networkRecord.isBlocked) {
+ logWtf("Network blocked for System Server: " + networkRecord.network);
+ return PRIORITY_ANY;
+ }
+
+ if (caps.hasTransport(TRANSPORT_CELLULAR)
+ && isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+ // If this carrier is the active data provider, ensure that opportunistic is only
+ // ever prioritized if it is also the active data subscription. This ensures that
+ // if an opportunistic subscription is still in the process of being switched to,
+ // or switched away from, the VCN does not attempt to continue using it against the
+ // decision made at the telephony layer. Failure to do so may result in the modem
+ // switching back and forth.
+ //
+ // Allow the following two cases:
+ // 1. Active subId is NOT in the group that this VCN is supporting
+ // 2. This opportunistic subscription is for the active subId
+ if (!snapshot.getAllSubIdsInGroup(subscriptionGroup)
+ .contains(SubscriptionManager.getActiveDataSubscriptionId())
+ || caps.getSubscriptionIds()
+ .contains(SubscriptionManager.getActiveDataSubscriptionId())) {
+ return PRIORITY_OPPORTUNISTIC_CELLULAR;
+ }
+ }
+
+ if (caps.hasTransport(TRANSPORT_WIFI)) {
+ if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)
+ && currentlySelected != null
+ && networkRecord.network.equals(currentlySelected.network)) {
+ return PRIORITY_WIFI_IN_USE;
+ }
+
+ if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
+ return PRIORITY_WIFI_PROSPECTIVE;
+ }
+ }
+
+ // Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might
+ // be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be
+ // the case if the Default Data SubId does not support certain services (eg voice
+ // calling)
+ if (caps.hasTransport(TRANSPORT_CELLULAR)
+ && !isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+ return PRIORITY_MACRO_CELLULAR;
+ }
+
+ return PRIORITY_ANY;
+ }
+
+ static boolean isOpportunistic(
+ @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) {
+ if (snapshot == null) {
+ logWtf("Got null snapshot");
+ return false;
+ }
+ for (int subId : subIds) {
+ if (snapshot.isOpportunistic(subId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ if (carrierConfig != null) {
+ return carrierConfig.getInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+ WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT);
+ }
+ return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
+ }
+
+ static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ if (carrierConfig != null) {
+ return carrierConfig.getInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+ WIFI_EXIT_RSSI_THRESHOLD_DEFAULT);
+ }
+ return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
+ }
+
+ static String priorityClassToString(int priorityClass) {
+ return PRIORITY_TO_STRING_MAP.get(priorityClass, "unknown");
+ }
+
+ private static void logWtf(String msg) {
+ Slog.wtf(TAG, msg);
+ LOCAL_LOG.log(TAG + " WTF: " + msg);
+ }
+}
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
similarity index 60%
rename from services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
rename to services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 7ddd135..071c7a6 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.server.vcn;
+package com.android.server.vcn.routeselection;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
import static com.android.server.VcnManagementService.LOCAL_LOG;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.isOpportunistic;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,27 +32,23 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Slog;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -61,68 +58,18 @@
/**
* Tracks a set of Networks underpinning a VcnGatewayConnection.
*
- * <p>A single UnderlyingNetworkTracker is built to serve a SINGLE VCN Gateway Connection, and MUST
- * be torn down with the VcnGatewayConnection in order to ensure underlying networks are allowed to
- * be reaped.
+ * <p>A single UnderlyingNetworkController is built to serve a SINGLE VCN Gateway Connection, and
+ * MUST be torn down with the VcnGatewayConnection in order to ensure underlying networks are
+ * allowed to be reaped.
*
* @hide
*/
-public class UnderlyingNetworkTracker {
- @NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
-
- /**
- * Minimum signal strength for a WiFi network to be eligible for switching to
- *
- * <p>A network that satisfies this is eligible to become the selected underlying network with
- * no additional conditions
- */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70;
-
- /**
- * Minimum signal strength to continue using a WiFi network
- *
- * <p>A network that satisfies the conditions may ONLY continue to be used if it is already
- * selected as the underlying network. A WiFi network satisfying this condition, but NOT the
- * prospective-network RSSI threshold CANNOT be switched to.
- */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
-
- /** Priority for any cellular network for which the subscription is listed as opportunistic */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0;
-
- /** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_WIFI_IN_USE = 1;
-
- /** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_WIFI_PROSPECTIVE = 2;
-
- /** Priority for any standard macro cellular network */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_MACRO_CELLULAR = 3;
-
- /** Priority for any other networks (including unvalidated, etc) */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_ANY = Integer.MAX_VALUE;
-
- private static final SparseArray<String> PRIORITY_TO_STRING_MAP = new SparseArray<>();
-
- static {
- PRIORITY_TO_STRING_MAP.put(
- PRIORITY_OPPORTUNISTIC_CELLULAR, "PRIORITY_OPPORTUNISTIC_CELLULAR");
- PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_IN_USE, "PRIORITY_WIFI_IN_USE");
- PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_PROSPECTIVE, "PRIORITY_WIFI_PROSPECTIVE");
- PRIORITY_TO_STRING_MAP.put(PRIORITY_MACRO_CELLULAR, "PRIORITY_MACRO_CELLULAR");
- PRIORITY_TO_STRING_MAP.put(PRIORITY_ANY, "PRIORITY_ANY");
- }
+public class UnderlyingNetworkController {
+ @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName();
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
- @NonNull private final UnderlyingNetworkTrackerCallback mCb;
+ @NonNull private final UnderlyingNetworkControllerCallback mCb;
@NonNull private final Dependencies mDeps;
@NonNull private final Handler mHandler;
@NonNull private final ConnectivityManager mConnectivityManager;
@@ -142,11 +89,11 @@
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
- public UnderlyingNetworkTracker(
+ public UnderlyingNetworkController(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull UnderlyingNetworkTrackerCallback cb) {
+ @NonNull UnderlyingNetworkControllerCallback cb) {
this(
vcnContext,
subscriptionGroup,
@@ -155,11 +102,11 @@
new Dependencies());
}
- private UnderlyingNetworkTracker(
+ private UnderlyingNetworkController(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull UnderlyingNetworkTrackerCallback cb,
+ @NonNull UnderlyingNetworkControllerCallback cb,
@NonNull Dependencies deps) {
mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
@@ -271,8 +218,8 @@
* subscription group, while the VCN networks are excluded by virtue of not having subIds set on
* the VCN-exposed networks.
*
- * <p>If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return
- * a NetworkRequest that only matches Test Networks.
+ * <p>If the VCN that this UnderlyingNetworkController belongs to is in test-mode, this will
+ * return a NetworkRequest that only matches Test Networks.
*/
private NetworkRequest getRouteSelectionRequest() {
if (mVcnContext.isInTestMode()) {
@@ -373,9 +320,9 @@
}
/**
- * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
+ * Update this UnderlyingNetworkController's TelephonySubscriptionSnapshot.
*
- * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkTracker to
+ * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkController to
* reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
* or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
*/
@@ -410,7 +357,7 @@
private void reevaluateNetworks() {
if (mIsQuitting || mRouteSelectionCallback == null) {
- return; // UnderlyingNetworkTracker has quit.
+ return; // UnderlyingNetworkController has quit.
}
TreeSet<UnderlyingNetworkRecord> sorted =
@@ -424,22 +371,6 @@
mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
}
- private static boolean isOpportunistic(
- @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) {
- if (snapshot == null) {
- logWtf("Got null snapshot");
- return false;
- }
-
- for (int subId : subIds) {
- if (snapshot.isOpportunistic(subId)) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped.
*
@@ -544,230 +475,6 @@
}
}
- private static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
- if (carrierConfig != null) {
- return carrierConfig.getInt(
- VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
- WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT);
- }
-
- return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
- }
-
- private static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
- if (carrierConfig != null) {
- return carrierConfig.getInt(
- VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
- WIFI_EXIT_RSSI_THRESHOLD_DEFAULT);
- }
-
- return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
- }
-
- /** A record of a single underlying network, caching relevant fields. */
- public static class UnderlyingNetworkRecord {
- @NonNull public final Network network;
- @NonNull public final NetworkCapabilities networkCapabilities;
- @NonNull public final LinkProperties linkProperties;
- public final boolean isBlocked;
-
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- UnderlyingNetworkRecord(
- @NonNull Network network,
- @NonNull NetworkCapabilities networkCapabilities,
- @NonNull LinkProperties linkProperties,
- boolean isBlocked) {
- this.network = network;
- this.networkCapabilities = networkCapabilities;
- this.linkProperties = linkProperties;
- this.isBlocked = isBlocked;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof UnderlyingNetworkRecord)) return false;
- final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o;
-
- return network.equals(that.network)
- && networkCapabilities.equals(that.networkCapabilities)
- && linkProperties.equals(that.linkProperties)
- && isBlocked == that.isBlocked;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
- }
-
- /**
- * Gives networks a priority class, based on the following priorities:
- *
- * <ol>
- * <li>Opportunistic cellular
- * <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT
- * <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT
- * <li>Macro cellular
- * <li>Any others
- * </ol>
- */
- private int calculatePriorityClass(
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
- final NetworkCapabilities caps = networkCapabilities;
-
- // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
-
- if (isBlocked) {
- logWtf("Network blocked for System Server: " + network);
- return PRIORITY_ANY;
- }
-
- if (caps.hasTransport(TRANSPORT_CELLULAR)
- && isOpportunistic(snapshot, caps.getSubscriptionIds())) {
- // If this carrier is the active data provider, ensure that opportunistic is only
- // ever prioritized if it is also the active data subscription. This ensures that
- // if an opportunistic subscription is still in the process of being switched to,
- // or switched away from, the VCN does not attempt to continue using it against the
- // decision made at the telephony layer. Failure to do so may result in the modem
- // switching back and forth.
- //
- // Allow the following two cases:
- // 1. Active subId is NOT in the group that this VCN is supporting
- // 2. This opportunistic subscription is for the active subId
- if (!snapshot.getAllSubIdsInGroup(subscriptionGroup)
- .contains(SubscriptionManager.getActiveDataSubscriptionId())
- || caps.getSubscriptionIds()
- .contains(SubscriptionManager.getActiveDataSubscriptionId())) {
- return PRIORITY_OPPORTUNISTIC_CELLULAR;
- }
- }
-
- if (caps.hasTransport(TRANSPORT_WIFI)) {
- if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)
- && currentlySelected != null
- && network.equals(currentlySelected.network)) {
- return PRIORITY_WIFI_IN_USE;
- }
-
- if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
- return PRIORITY_WIFI_PROSPECTIVE;
- }
- }
-
- // Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might
- // be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be
- // the case if the Default Data SubId does not support certain services (eg voice
- // calling)
- if (caps.hasTransport(TRANSPORT_CELLULAR)
- && !isOpportunistic(snapshot, caps.getSubscriptionIds())) {
- return PRIORITY_MACRO_CELLULAR;
- }
-
- return PRIORITY_ANY;
- }
-
- private static Comparator<UnderlyingNetworkRecord> getComparator(
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
- return (left, right) -> {
- return Integer.compare(
- left.calculatePriorityClass(
- subscriptionGroup, snapshot, currentlySelected, carrierConfig),
- right.calculatePriorityClass(
- subscriptionGroup, snapshot, currentlySelected, carrierConfig));
- };
- }
-
- /** Dumps the state of this record for logging and debugging purposes. */
- private void dump(
- IndentingPrintWriter pw,
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
- pw.println("UnderlyingNetworkRecord:");
- pw.increaseIndent();
-
- final int priorityClass =
- calculatePriorityClass(
- subscriptionGroup, snapshot, currentlySelected, carrierConfig);
- pw.println(
- "Priority class: " + PRIORITY_TO_STRING_MAP.get(priorityClass) + " ("
- + priorityClass + ")");
- pw.println("mNetwork: " + network);
- pw.println("mNetworkCapabilities: " + networkCapabilities);
- pw.println("mLinkProperties: " + linkProperties);
-
- pw.decreaseIndent();
- }
-
- /** Builder to incrementally construct an UnderlyingNetworkRecord. */
- private static class Builder {
- @NonNull private final Network mNetwork;
-
- @Nullable private NetworkCapabilities mNetworkCapabilities;
- @Nullable private LinkProperties mLinkProperties;
- boolean mIsBlocked;
- boolean mWasIsBlockedSet;
-
- @Nullable private UnderlyingNetworkRecord mCached;
-
- private Builder(@NonNull Network network) {
- mNetwork = network;
- }
-
- @NonNull
- private Network getNetwork() {
- return mNetwork;
- }
-
- private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
- mNetworkCapabilities = networkCapabilities;
- mCached = null;
- }
-
- @Nullable
- private NetworkCapabilities getNetworkCapabilities() {
- return mNetworkCapabilities;
- }
-
- private void setLinkProperties(@NonNull LinkProperties linkProperties) {
- mLinkProperties = linkProperties;
- mCached = null;
- }
-
- private void setIsBlocked(boolean isBlocked) {
- mIsBlocked = isBlocked;
- mWasIsBlockedSet = true;
- mCached = null;
- }
-
- private boolean isValid() {
- return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
- }
-
- private UnderlyingNetworkRecord build() {
- if (!isValid()) {
- throw new IllegalArgumentException(
- "Called build before UnderlyingNetworkRecord was valid");
- }
-
- if (mCached == null) {
- mCached =
- new UnderlyingNetworkRecord(
- mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
- }
-
- return mCached;
- }
- }
- }
-
private static void logWtf(String msg) {
Slog.wtf(TAG, msg);
LOCAL_LOG.log(TAG + " WTF: " + msg);
@@ -780,7 +487,7 @@
/** Dumps the state of this record for logging and debugging purposes. */
public void dump(IndentingPrintWriter pw) {
- pw.println("UnderlyingNetworkTracker:");
+ pw.println("UnderlyingNetworkController:");
pw.increaseIndent();
pw.println("Carrier WiFi Entry Threshold: " + getWifiEntryRssiThreshold(mCarrierConfig));
@@ -811,7 +518,7 @@
}
/** Callbacks for being notified of the changes in, or to the selected underlying network. */
- public interface UnderlyingNetworkTrackerCallback {
+ public interface UnderlyingNetworkControllerCallback {
/**
* Fired when a new underlying network is selected, or properties have changed.
*
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
new file mode 100644
index 0000000..65c69de
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -0,0 +1,175 @@
+/*
+ * 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.server.vcn.routeselection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * A record of a single underlying network, caching relevant fields.
+ *
+ * @hide
+ */
+public class UnderlyingNetworkRecord {
+ @NonNull public final Network network;
+ @NonNull public final NetworkCapabilities networkCapabilities;
+ @NonNull public final LinkProperties linkProperties;
+ public final boolean isBlocked;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public UnderlyingNetworkRecord(
+ @NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties,
+ boolean isBlocked) {
+ this.network = network;
+ this.networkCapabilities = networkCapabilities;
+ this.linkProperties = linkProperties;
+ this.isBlocked = isBlocked;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof UnderlyingNetworkRecord)) return false;
+ final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o;
+
+ return network.equals(that.network)
+ && networkCapabilities.equals(that.networkCapabilities)
+ && linkProperties.equals(that.linkProperties)
+ && isBlocked == that.isBlocked;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
+ }
+
+ static Comparator<UnderlyingNetworkRecord> getComparator(
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ return (left, right) -> {
+ return Integer.compare(
+ NetworkPriorityClassifier.calculatePriorityClass(
+ left, subscriptionGroup, snapshot, currentlySelected, carrierConfig),
+ NetworkPriorityClassifier.calculatePriorityClass(
+ right, subscriptionGroup, snapshot, currentlySelected, carrierConfig));
+ };
+ }
+
+ /** Dumps the state of this record for logging and debugging purposes. */
+ void dump(
+ IndentingPrintWriter pw,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundle carrierConfig) {
+ pw.println("UnderlyingNetworkRecord:");
+ pw.increaseIndent();
+
+ final int priorityClass =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ this, subscriptionGroup, snapshot, currentlySelected, carrierConfig);
+ pw.println(
+ "Priority class: "
+ + NetworkPriorityClassifier.priorityClassToString(priorityClass)
+ + " ("
+ + priorityClass
+ + ")");
+ pw.println("mNetwork: " + network);
+ pw.println("mNetworkCapabilities: " + networkCapabilities);
+ pw.println("mLinkProperties: " + linkProperties);
+
+ pw.decreaseIndent();
+ }
+
+ /** Builder to incrementally construct an UnderlyingNetworkRecord. */
+ static class Builder {
+ @NonNull private final Network mNetwork;
+
+ @Nullable private NetworkCapabilities mNetworkCapabilities;
+ @Nullable private LinkProperties mLinkProperties;
+ boolean mIsBlocked;
+ boolean mWasIsBlockedSet;
+
+ @Nullable private UnderlyingNetworkRecord mCached;
+
+ Builder(@NonNull Network network) {
+ mNetwork = network;
+ }
+
+ @NonNull
+ Network getNetwork() {
+ return mNetwork;
+ }
+
+ void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ mCached = null;
+ }
+
+ @Nullable
+ NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
+
+ void setLinkProperties(@NonNull LinkProperties linkProperties) {
+ mLinkProperties = linkProperties;
+ mCached = null;
+ }
+
+ void setIsBlocked(boolean isBlocked) {
+ mIsBlocked = isBlocked;
+ mWasIsBlockedSet = true;
+ mCached = null;
+ }
+
+ boolean isValid() {
+ return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
+ }
+
+ UnderlyingNetworkRecord build() {
+ if (!isValid()) {
+ throw new IllegalArgumentException(
+ "Called build before UnderlyingNetworkRecord was valid");
+ }
+
+ if (mCached == null) {
+ mCached =
+ new UnderlyingNetworkRecord(
+ mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+ }
+
+ return mCached;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index bc21f1b..f82f99d 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,6 +16,14 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IUidObserver;
@@ -90,6 +98,8 @@
@GuardedBy("mLock")
private int mHapticFeedbackIntensity;
@GuardedBy("mLock")
+ private int mHardwareFeedbackIntensity;
+ @GuardedBy("mLock")
private int mNotificationIntensity;
@GuardedBy("mLock")
private int mRingIntensity;
@@ -176,7 +186,7 @@
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
- registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER));
+ registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER));
registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.ZEN_MODE));
registerSettingsObserver(
Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY));
@@ -232,17 +242,20 @@
* @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
*/
public int getDefaultIntensity(int usageHint) {
- if (isAlarm(usageHint)) {
+ if (usageHint == USAGE_ALARM) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
}
synchronized (mLock) {
if (mVibrator != null) {
- if (isRingtone(usageHint)) {
- return mVibrator.getDefaultRingVibrationIntensity();
- } else if (isNotification(usageHint)) {
- return mVibrator.getDefaultNotificationVibrationIntensity();
- } else if (isHapticFeedback(usageHint)) {
- return mVibrator.getDefaultHapticFeedbackIntensity();
+ switch (usageHint) {
+ case USAGE_RINGTONE:
+ return mVibrator.getDefaultRingVibrationIntensity();
+ case USAGE_NOTIFICATION:
+ return mVibrator.getDefaultNotificationVibrationIntensity();
+ case USAGE_TOUCH:
+ case USAGE_HARDWARE_FEEDBACK:
+ case USAGE_PHYSICAL_EMULATION:
+ return mVibrator.getDefaultHapticFeedbackIntensity();
}
}
}
@@ -257,16 +270,20 @@
*/
public int getCurrentIntensity(int usageHint) {
synchronized (mLock) {
- if (isRingtone(usageHint)) {
- return mRingIntensity;
- } else if (isNotification(usageHint)) {
- return mNotificationIntensity;
- } else if (isHapticFeedback(usageHint)) {
- return mHapticFeedbackIntensity;
- } else if (isAlarm(usageHint)) {
- return Vibrator.VIBRATION_INTENSITY_HIGH;
- } else {
- return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+ switch (usageHint) {
+ case USAGE_RINGTONE:
+ return mRingIntensity;
+ case USAGE_NOTIFICATION:
+ return mNotificationIntensity;
+ case USAGE_TOUCH:
+ return mHapticFeedbackIntensity;
+ case USAGE_HARDWARE_FEEDBACK:
+ case USAGE_PHYSICAL_EMULATION:
+ return mHardwareFeedbackIntensity;
+ case USAGE_ALARM:
+ return Vibrator.VIBRATION_INTENSITY_HIGH;
+ default:
+ return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
}
}
@@ -289,7 +306,7 @@
* for ringtone usage only. All other usages are allowed independently of ringer mode.
*/
public boolean shouldVibrateForRingerMode(int usageHint) {
- if (!isRingtone(usageHint)) {
+ if (usageHint != USAGE_RINGTONE) {
return true;
}
synchronized (mLock) {
@@ -324,8 +341,10 @@
* {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST} usages are allowed to vibrate.
*/
public boolean shouldVibrateForPowerMode(int usageHint) {
- return !mLowPowerMode || isRingtone(usageHint) || isAlarm(usageHint)
- || usageHint == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+ synchronized (mLock) {
+ return !mLowPowerMode || usageHint == USAGE_RINGTONE || usageHint == USAGE_ALARM
+ || usageHint == USAGE_COMMUNICATION_REQUEST;
+ }
}
/** Return {@code true} if input devices should vibrate instead of this device. */
@@ -338,22 +357,6 @@
return mZenMode != Settings.Global.ZEN_MODE_OFF;
}
- private static boolean isNotification(int usageHint) {
- return usageHint == VibrationAttributes.USAGE_NOTIFICATION;
- }
-
- private static boolean isRingtone(int usageHint) {
- return usageHint == VibrationAttributes.USAGE_RINGTONE;
- }
-
- private static boolean isHapticFeedback(int usageHint) {
- return usageHint == VibrationAttributes.USAGE_TOUCH;
- }
-
- private static boolean isAlarm(int usageHint) {
- return usageHint == VibrationAttributes.USAGE_ALARM;
- }
-
private static boolean isClassAlarm(int usageHint) {
return (usageHint & VibrationAttributes.USAGE_CLASS_MASK)
== VibrationAttributes.USAGE_CLASS_ALARM;
@@ -363,20 +366,37 @@
public void updateSettings() {
synchronized (mLock) {
mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
- mApplyRampingRinger = getGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0) != 0;
+ mApplyRampingRinger = getSystemSetting(Settings.System.APPLY_RAMPING_RINGER, 0) != 0;
mHapticFeedbackIntensity = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
+ getDefaultIntensity(USAGE_TOUCH));
+ mHardwareFeedbackIntensity = getSystemSetting(
+ Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+ getHardwareFeedbackIntensityWhenSettingIsMissing(mHapticFeedbackIntensity));
mNotificationIntensity = getSystemSetting(
Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
+ getDefaultIntensity(USAGE_NOTIFICATION));
mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ getDefaultIntensity(USAGE_RINGTONE));
mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
}
notifyListeners();
}
+ /**
+ * Return the value to be used for {@link Settings.System#HARDWARE_HAPTIC_FEEDBACK_INTENSITY}
+ * when the value was not set by the user.
+ *
+ * <p>This should adapt the behavior preceding the introduction of this new setting key, which
+ * is to apply {@link Settings.System#HAPTIC_FEEDBACK_INTENSITY} unless it's disabled.
+ */
+ private int getHardwareFeedbackIntensityWhenSettingIsMissing(int hapticFeedbackIntensity) {
+ if (hapticFeedbackIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ return getDefaultIntensity(USAGE_HARDWARE_FEEDBACK);
+ }
+ return hapticFeedbackIntensity;
+ }
+
@Override
public String toString() {
return "VibrationSettings{"
@@ -389,18 +409,20 @@
+ ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude()
+ ", mRampStepDuration=" + mRampStepDuration
+ ", mRampDownDuration=" + mRampDownDuration
+ + ", mHardwareHapticFeedbackIntensity="
+ + intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK))
+ ", mHapticFeedbackIntensity="
- + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH))
+ + intensityToString(getCurrentIntensity(USAGE_TOUCH))
+ ", mHapticFeedbackDefaultIntensity="
- + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_TOUCH))
+ + intensityToString(getDefaultIntensity(USAGE_TOUCH))
+ ", mNotificationIntensity="
- + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION))
+ + intensityToString(getCurrentIntensity(USAGE_NOTIFICATION))
+ ", mNotificationDefaultIntensity="
- + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION))
+ + intensityToString(getDefaultIntensity(USAGE_NOTIFICATION))
+ ", mRingIntensity="
- + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE))
+ + intensityToString(getCurrentIntensity(USAGE_RINGTONE))
+ ", mRingDefaultIntensity="
- + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE))
+ + intensityToString(getDefaultIntensity(USAGE_RINGTONE))
+ '}';
}
@@ -410,15 +432,15 @@
proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
mHapticFeedbackIntensity);
proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
+ getDefaultIntensity(USAGE_TOUCH));
proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_INTENSITY,
mNotificationIntensity);
proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
+ getDefaultIntensity(USAGE_NOTIFICATION));
proto.write(VibratorManagerServiceDumpProto.RING_INTENSITY,
mRingIntensity);
proto.write(VibratorManagerServiceDumpProto.RING_DEFAULT_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ getDefaultIntensity(USAGE_RINGTONE));
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index a327382..fdd9913 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -287,6 +287,9 @@
}
// If we waited, the queue may have changed, so let the loop run again.
if (waitTime <= 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "Play vibration consuming next step...");
+ }
mStepQueue.consumeNext();
}
Vibration.Status status = mStop ? Vibration.Status.CANCELLED
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 4b7fd90..4a1b95b 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -40,21 +40,22 @@
private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200;
private final Object mLock = new Object();
- private final NativeWrapper mNativeWrapper;
@GuardedBy("mLock")
- private VibratorInfo mVibratorInfo;
- @GuardedBy("mLock")
- private boolean mVibratorInfoLoadSuccessful;
- @GuardedBy("mLock")
+ private final NativeWrapper mNativeWrapper;
+
+ // Vibrator state listeners that support concurrent updates and broadcasts, but should lock
+ // while broadcasting to guarantee delivery order.
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
new RemoteCallbackList<>();
- @GuardedBy("mLock")
- private boolean mIsVibrating;
- @GuardedBy("mLock")
- private boolean mIsUnderExternalControl;
- @GuardedBy("mLock")
- private float mCurrentAmplitude;
+
+ // Vibrator state variables that are updated from synchronized blocks but can be read anytime
+ // for a snippet of the current known vibrator state/info.
+ private volatile VibratorInfo mVibratorInfo;
+ private volatile boolean mVibratorInfoLoadSuccessful;
+ private volatile boolean mIsVibrating;
+ private volatile boolean mIsUnderExternalControl;
+ private volatile float mCurrentAmplitude;
/** Listener for vibration completion callbacks from native. */
public interface OnVibrationCompleteListener {
@@ -86,35 +87,39 @@
/** Register state listener for this vibrator. */
public boolean registerVibratorStateListener(IVibratorStateListener listener) {
- synchronized (mLock) {
- final long token = Binder.clearCallingIdentity();
- try {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Register the listener and send the first state atomically, to avoid potentially
+ // out of order broadcasts in between.
+ synchronized (mLock) {
if (!mVibratorStateListeners.register(listener)) {
return false;
}
// Notify its callback after new client registered.
- notifyStateListenerLocked(listener);
- return true;
- } finally {
- Binder.restoreCallingIdentity(token);
+ notifyStateListener(listener, mIsVibrating);
}
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
/** Remove registered state listener for this vibrator. */
public boolean unregisterVibratorStateListener(IVibratorStateListener listener) {
- synchronized (mLock) {
- final long token = Binder.clearCallingIdentity();
- try {
- return mVibratorStateListeners.unregister(listener);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mVibratorStateListeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
/** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */
public void reloadVibratorInfoIfNeeded() {
+ // Early check outside lock, for quick return.
+ if (mVibratorInfoLoadSuccessful) {
+ return;
+ }
synchronized (mLock) {
if (mVibratorInfoLoadSuccessful) {
return;
@@ -132,16 +137,12 @@
/** Checks if the {@link VibratorInfo} was loaded from the vibrator hardware successfully. */
boolean isVibratorInfoLoadSuccessful() {
- synchronized (mLock) {
- return mVibratorInfoLoadSuccessful;
- }
+ return mVibratorInfoLoadSuccessful;
}
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
public VibratorInfo getVibratorInfo() {
- synchronized (mLock) {
- return mVibratorInfo;
- }
+ return mVibratorInfo;
}
/**
@@ -151,9 +152,7 @@
* automatically notified to any registered {@link IVibratorStateListener} on change.
*/
public boolean isVibrating() {
- synchronized (mLock) {
- return mIsVibrating;
- }
+ return mIsVibrating;
}
/**
@@ -168,16 +167,12 @@
* <p>If {@link #isVibrating()} is false then this will be zero.
*/
public float getCurrentAmplitude() {
- synchronized (mLock) {
- return mCurrentAmplitude;
- }
+ return mCurrentAmplitude;
}
/** Return {@code true} if this vibrator is under external control, false otherwise. */
public boolean isUnderExternalControl() {
- synchronized (mLock) {
- return mIsUnderExternalControl;
- }
+ return mIsUnderExternalControl;
}
/**
@@ -187,14 +182,14 @@
* @return true if this vibrator has this capability, false otherwise
*/
public boolean hasCapability(long capability) {
- synchronized (mLock) {
- return mVibratorInfo.hasCapability(capability);
- }
+ return mVibratorInfo.hasCapability(capability);
}
/** Return {@code true} if the underlying vibrator is currently available, false otherwise. */
public boolean isAvailable() {
- return mNativeWrapper.isAvailable();
+ synchronized (mLock) {
+ return mNativeWrapper.isAvailable();
+ }
}
/**
@@ -203,10 +198,10 @@
* <p>This will affect the state of {@link #isUnderExternalControl()}.
*/
public void setExternalControl(boolean externalControl) {
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+ return;
+ }
synchronized (mLock) {
- if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
- return;
- }
mIsUnderExternalControl = externalControl;
mNativeWrapper.setExternalControl(externalControl);
}
@@ -217,10 +212,10 @@
* if given {@code effect} is {@code null}.
*/
public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) {
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
+ return;
+ }
synchronized (mLock) {
- if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
- return;
- }
if (prebaked == null) {
mNativeWrapper.alwaysOnDisable(id);
} else {
@@ -256,7 +251,7 @@
long duration = mNativeWrapper.on(milliseconds, vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
- notifyVibratorOnLocked();
+ notifyListenerOnVibrating(true);
}
return duration;
}
@@ -277,7 +272,7 @@
prebaked.getEffectStrength(), vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
- notifyVibratorOnLocked();
+ notifyListenerOnVibrating(true);
}
return duration;
}
@@ -293,14 +288,14 @@
* do not support the input or a negative number if the operation failed.
*/
public long on(PrimitiveSegment[] primitives, long vibrationId) {
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+ return 0;
+ }
synchronized (mLock) {
- if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
- return 0;
- }
long duration = mNativeWrapper.compose(primitives, vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
- notifyVibratorOnLocked();
+ notifyListenerOnVibrating(true);
}
return duration;
}
@@ -315,15 +310,15 @@
* @return The duration of the effect playing, or 0 if unsupported.
*/
public long on(RampSegment[] primitives, long vibrationId) {
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
+ return 0;
+ }
synchronized (mLock) {
- if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
- return 0;
- }
int braking = mVibratorInfo.getDefaultBraking();
long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
- notifyVibratorOnLocked();
+ notifyListenerOnVibrating(true);
}
return duration;
}
@@ -334,7 +329,7 @@
synchronized (mLock) {
mNativeWrapper.off();
mCurrentAmplitude = 0;
- notifyVibratorOffLocked();
+ notifyListenerOnVibrating(false);
}
}
@@ -349,51 +344,31 @@
@Override
public String toString() {
- synchronized (mLock) {
- return "VibratorController{"
- + "mVibratorInfo=" + mVibratorInfo
- + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
- + ", mIsVibrating=" + mIsVibrating
- + ", mCurrentAmplitude=" + mCurrentAmplitude
- + ", mIsUnderExternalControl=" + mIsUnderExternalControl
- + ", mVibratorStateListeners count="
- + mVibratorStateListeners.getRegisteredCallbackCount()
- + '}';
- }
+ return "VibratorController{"
+ + "mVibratorInfo=" + mVibratorInfo
+ + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
+ + ", mIsVibrating=" + mIsVibrating
+ + ", mCurrentAmplitude=" + mCurrentAmplitude
+ + ", mIsUnderExternalControl=" + mIsUnderExternalControl
+ + ", mVibratorStateListeners count="
+ + mVibratorStateListeners.getRegisteredCallbackCount()
+ + '}';
}
@GuardedBy("mLock")
- private void notifyVibratorOnLocked() {
- if (!mIsVibrating) {
- mIsVibrating = true;
- notifyStateListenersLocked();
+ private void notifyListenerOnVibrating(boolean isVibrating) {
+ if (mIsVibrating != isVibrating) {
+ mIsVibrating = isVibrating;
+ // The broadcast method is safe w.r.t. register/unregister listener methods, but lock
+ // is required here to guarantee delivery order.
+ mVibratorStateListeners.broadcast(
+ listener -> notifyStateListener(listener, isVibrating));
}
}
- @GuardedBy("mLock")
- private void notifyVibratorOffLocked() {
- if (mIsVibrating) {
- mIsVibrating = false;
- notifyStateListenersLocked();
- }
- }
-
- @GuardedBy("mLock")
- private void notifyStateListenersLocked() {
- final int length = mVibratorStateListeners.beginBroadcast();
+ private void notifyStateListener(IVibratorStateListener listener, boolean isVibrating) {
try {
- for (int i = 0; i < length; i++) {
- notifyStateListenerLocked(mVibratorStateListeners.getBroadcastItem(i));
- }
- } finally {
- mVibratorStateListeners.finishBroadcast();
- }
- }
-
- @GuardedBy("mLock")
- private void notifyStateListenerLocked(IVibratorStateListener listener) {
- try {
- listener.onVibrating(mIsVibrating);
+ listener.onVibrating(isVibrating);
} catch (RemoteException | RuntimeException e) {
Slog.e(TAG, "Vibrator state listener failed to call", e);
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 0675ad6..9717201 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -341,7 +341,7 @@
if (!isEffectValid(effect)) {
return false;
}
- attrs = fixupVibrationAttributes(attrs);
+ attrs = fixupVibrationAttributes(attrs, effect);
synchronized (mLock) {
SparseArray<PrebakedSegment> effects = fixupAlwaysOnEffectsLocked(effect);
if (effects == null) {
@@ -385,12 +385,15 @@
if (!isEffectValid(effect)) {
return null;
}
- attrs = fixupVibrationAttributes(attrs);
+ attrs = fixupVibrationAttributes(attrs, effect);
Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
uid, opPkg, reason);
fillVibrationFallbacks(vib, effect);
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
+ }
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
@@ -498,6 +501,9 @@
@VisibleForTesting
void updateServiceState() {
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Updating device state...");
+ }
boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators(
mVibrationSettings.shouldVibrateInputDevices());
@@ -611,6 +617,9 @@
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
try {
Vibration vib = mCurrentVibration.getVibration();
+ if (DEBUG) {
+ Slog.d(TAG, "Reporting vibration " + vib.id + " finished with status " + status);
+ }
endVibrationLocked(vib, status);
finishAppOpModeLocked(vib.uid, vib.opPkg);
} finally {
@@ -886,21 +895,32 @@
* Return new {@link VibrationAttributes} that only applies flags that this user has permissions
* to use.
*/
- private VibrationAttributes fixupVibrationAttributes(@Nullable VibrationAttributes attrs) {
+ @NonNull
+ private VibrationAttributes fixupVibrationAttributes(@Nullable VibrationAttributes attrs,
+ CombinedVibration effect) {
if (attrs == null) {
attrs = DEFAULT_ATTRIBUTES;
}
+ int usage = attrs.getUsage();
+ if ((usage == VibrationAttributes.USAGE_UNKNOWN) && effect.isHapticFeedbackCandidate()) {
+ usage = VibrationAttributes.USAGE_TOUCH;
+ }
+ int flags = attrs.getFlags();
if (attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
|| hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
|| hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
- final int flags = attrs.getFlags()
- & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
- attrs = new VibrationAttributes.Builder(attrs)
- .setFlags(flags, attrs.getFlags()).build();
+ // Remove bypass policy flag from attributes if the app does not have permissions.
+ flags &= ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
}
}
- return attrs;
+ if ((usage == attrs.getUsage()) && (flags == attrs.getFlags())) {
+ return attrs;
+ }
+ return new VibrationAttributes.Builder(attrs)
+ .setUsage(usage)
+ .setFlags(flags, attrs.getFlags())
+ .build();
}
@GuardedBy("mLock")
@@ -1062,11 +1082,17 @@
Slog.d(TAG, "Vibrators released after finished vibration");
}
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Processing vibrators released callback");
+ }
mCurrentVibration = null;
if (mNextVibration != null) {
VibrationThread vibThread = mNextVibration;
mNextVibration = null;
- startVibrationThreadLocked(vibThread);
+ Vibration.Status status = startVibrationThreadLocked(vibThread);
+ if (status != Vibration.Status.RUNNING) {
+ endVibrationLocked(vibThread.getVibration(), status);
+ }
}
}
}
@@ -1248,6 +1274,9 @@
void dumpText(PrintWriter pw) {
pw.println("Vibrator Manager Service:");
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Dumping vibrator manager service to text...");
+ }
pw.println(" mVibrationSettings:");
pw.println(" " + mVibrationSettings);
pw.println();
@@ -1290,6 +1319,9 @@
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Dumping vibrator manager service to proto...");
+ }
mVibrationSettings.dumpProto(proto);
if (mCurrentVibration != null) {
mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ea22d92..100c44b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1376,9 +1376,7 @@
/** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */
private boolean shouldStartChangeTransition(
@Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) {
- if (mWmService.mDisableTransitionAnimation
- || mDisplayContent == null || newParent == null || oldParent == null
- || getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) {
+ if (newParent == null || oldParent == null || !canStartChangeTransition()) {
return false;
}
@@ -6224,15 +6222,15 @@
public boolean inputDispatchingTimedOut(String reason, int windowPid) {
ActivityRecord anrActivity;
WindowProcessController anrApp;
- boolean windowFromSameProcessAsActivity;
+ boolean blameActivityProcess;
synchronized (mAtmService.mGlobalLock) {
anrActivity = getWaitingHistoryRecordLocked();
anrApp = app;
- windowFromSameProcessAsActivity =
- !hasProcess() || app.getPid() == windowPid || windowPid == INVALID_PID;
+ blameActivityProcess = hasProcess()
+ && (app.getPid() == windowPid || windowPid == INVALID_PID);
}
- if (windowFromSameProcessAsActivity) {
+ if (blameActivityProcess) {
return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
anrActivity.shortComponentName, anrActivity.info.applicationInfo,
shortComponentName, app, false, reason);
@@ -6434,6 +6432,13 @@
} else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON) {
return false;
}
+ // Choose the default behavior for Launcher and SystemUI when the SplashScreen style is
+ // not specified in the ActivityOptions.
+ if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME) {
+ return false;
+ } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+ return true;
+ }
}
if (sourceRecord == null) {
sourceRecord = searchCandidateLaunchingActivity();
@@ -6443,11 +6448,11 @@
return sourceRecord.mSplashScreenStyleEmpty;
}
- // If this activity was launched from a system surface for first start, never use an empty
- // splash screen. Need to check sourceRecord before in case this activity is launched from
- // service.
- // Otherwise use empty.
- return !startActivity || !launchedFromSystemSurface();
+ // If this activity was launched from Launcher or System for first start, never use an
+ // empty splash screen.
+ // Need to check sourceRecord before in case this activity is launched from service.
+ return !startActivity || !(mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM
+ || mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME);
}
private int getSplashscreenTheme() {
@@ -7778,9 +7783,17 @@
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
final boolean fillContainer = resolvedBounds.equals(containingBounds);
final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
- final int screenPosY = mSizeCompatBounds == null
+ // If the activity is not in size compat mode, calculate vertical centering
+ // from the container and resolved bounds.
+ // If the activity is in size compat mode, calculate vertical centering
+ // from the container and size compat bounds.
+ // The container bounds contain the parent bounds offset in the display, for
+ // example when an activity is in the lower split of split screen.
+ final int screenPosY = (mSizeCompatBounds == null
? (containerBounds.height() - resolvedBounds.height()) / 2
- : (containerBounds.height() - mSizeCompatBounds.height()) / 2;
+ : (containerBounds.height() - mSizeCompatBounds.height()) / 2)
+ + containerBounds.top;
+
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
mSizeCompatBounds.offset(screenPosX, screenPosY);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 210b0ae..ecc8587 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -496,11 +496,13 @@
* @param activityIntent intent to start the activity.
* @param activityOptions ActivityOptions to start the activity with.
* @param resultTo the caller activity
+ * @param callingUid the caller uid
+ * @param callingPid the caller pid
* @return the start result.
*/
int startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
@NonNull Intent activityIntent, @Nullable Bundle activityOptions,
- @Nullable IBinder resultTo) {
+ @Nullable IBinder resultTo, int callingUid, int callingPid) {
final ActivityRecord caller =
resultTo != null ? ActivityRecord.forTokenLocked(resultTo) : null;
return obtainStarter(activityIntent, "startActivityInTaskFragment")
@@ -508,8 +510,8 @@
.setInTaskFragment(taskFragment)
.setResultTo(resultTo)
.setRequestCode(-1)
- .setCallingUid(Binder.getCallingUid())
- .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
.setUserId(caller != null ? caller.mUserId : mService.getCurrentUserId())
.execute();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 73a783e..bb7434d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2784,8 +2784,8 @@
// If it exist, we need to reparent target root task from TDA to launch root task.
final TaskDisplayArea tda = mTargetRootTask.getDisplayArea();
final Task launchRootTask = tda.getLaunchRootTask(mTargetRootTask.getWindowingMode(),
- mTargetRootTask.getActivityType(), null /** options */,
- mSourceRootTask, 0 /** launchFlags */);
+ mTargetRootTask.getActivityType(), null /** options */, mSourceRootTask,
+ mLaunchFlags);
// If target root task is created by organizer, let organizer handle reparent itself.
if (!mTargetRootTask.mCreatedByOrganizer && launchRootTask != null
&& launchRootTask != mTargetRootTask) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 7fa9861..e38e9c1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -126,35 +126,6 @@
}
/**
- * Sleep tokens cause the activity manager to put the top activity to sleep.
- * They are used by components such as dreams that may hide and block interaction
- * with underlying activities.
- * The Acquirer provides an interface that encapsulates the underlying work, so the user does
- * not need to handle the token by him/herself.
- */
- public interface SleepTokenAcquirer {
-
- /**
- * Acquires a sleep token.
- * @param displayId The display to apply to.
- */
- void acquire(int displayId);
-
- /**
- * Releases the sleep token.
- * @param displayId The display to apply to.
- */
- void release(int displayId);
- }
-
- /**
- * Creates a sleep token acquirer for the specified display with the specified tag.
- *
- * @param tag A string identifying the purpose (eg. "Dream").
- */
- public abstract SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag);
-
- /**
* Returns home activity for the specified user.
*
* @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0a85ba1..e4ed04de 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4544,17 +4544,25 @@
reason);
}
- final class SleepTokenAcquirerImpl implements ActivityTaskManagerInternal.SleepTokenAcquirer {
+ /**
+ * Sleep tokens cause the activity manager to put the top activity to sleep.
+ * They are used by components such as dreams that may hide and block interaction
+ * with underlying activities.
+ */
+ final class SleepTokenAcquirer {
private final String mTag;
private final SparseArray<RootWindowContainer.SleepToken> mSleepTokens =
new SparseArray<>();
- SleepTokenAcquirerImpl(@NonNull String tag) {
+ SleepTokenAcquirer(@NonNull String tag) {
mTag = tag;
}
- @Override
- public void acquire(int displayId) {
+ /**
+ * Acquires a sleep token.
+ * @param displayId The display to apply to.
+ */
+ void acquire(int displayId) {
synchronized (mGlobalLock) {
if (!mSleepTokens.contains(displayId)) {
mSleepTokens.append(displayId,
@@ -4564,8 +4572,11 @@
}
}
- @Override
- public void release(int displayId) {
+ /**
+ * Releases the sleep token.
+ * @param displayId The display to apply to.
+ */
+ void release(int displayId) {
synchronized (mGlobalLock) {
final RootWindowContainer.SleepToken token = mSleepTokens.get(displayId);
if (token != null) {
@@ -5255,12 +5266,6 @@
final class LocalService extends ActivityTaskManagerInternal {
@Override
- public SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag) {
- Objects.requireNonNull(tag);
- return new SleepTokenAcquirerImpl(tag);
- }
-
- @Override
public ComponentName getHomeActivityForUser(int userId) {
synchronized (mGlobalLock) {
final ActivityRecord homeActivity =
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index df9a6d2..721907c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -176,6 +176,9 @@
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
+ // TODO(b/205335975): Remove window which stuck in animatingExit status. Find actual cause.
+ mDisplayContent.forAllWindows(WindowState::cleanupAnimatingExitWindow,
+ true /* traverseTopToBottom */);
// TODO(new-app-transition): Remove code using appTransition.getAppTransition()
final AppTransition appTransition = mDisplayContent.mAppTransition;
@@ -590,7 +593,7 @@
}
// We don't want the organizer to handle transition of non-embedded activity of other
// app.
- if (r.getUid() != rootActivity.getUid() && !r.isEmbedded()) {
+ if (r.getUid() != task.effectiveUid && !r.isEmbedded()) {
leafTask = null;
break;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 42c8124..9d8f1fc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -34,6 +34,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Build.VERSION_CODES.N;
+import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.util.RotationUtils.deltaRotation;
@@ -43,6 +44,8 @@
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static android.view.Display.STATE_UNKNOWN;
+import static android.view.Display.isSuspendedState;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -61,6 +64,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
@@ -218,6 +222,7 @@
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
import com.android.internal.annotations.VisibleForTesting;
@@ -316,11 +321,6 @@
*/
private Rect mLastMirroredDisplayAreaBounds = null;
- /**
- * The last state of the display.
- */
- private int mLastDisplayState;
-
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
@@ -664,7 +664,7 @@
/** All tokens used to put activities on this root task to sleep (including mOffToken) */
final ArrayList<RootWindowContainer.SleepToken> mAllSleepTokens = new ArrayList<>();
/** The token acquirer to put root tasks on the display to sleep */
- private final ActivityTaskManagerInternal.SleepTokenAcquirer mOffTokenAcquirer;
+ private final ActivityTaskManagerService.SleepTokenAcquirer mOffTokenAcquirer;
private boolean mSleeping;
@@ -696,6 +696,14 @@
// well and thus won't change the top resumed / focused record
boolean mDontMoveToTop;
+ /**
+ * The policy controller of the windows that can be displayed on the virtual display.
+ *
+ * @see DisplayWindowPolicyController
+ */
+ @Nullable
+ DisplayWindowPolicyController mDisplayWindowPolicyController;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -1631,11 +1639,6 @@
// to cover the activity configuration change.
return false;
}
- if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) {
- // Currently it is unknown that when will IME window be ready. Reject the case to
- // avoid flickering by showing IME in inconsistent orientation.
- return false;
- }
if (checkOpening) {
if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
// Apply normal rotation animation in case of the activity set different requested
@@ -2739,6 +2742,9 @@
if (newDisplayInfo != null) {
mDisplayInfo.copyFrom(newDisplayInfo);
}
+
+ mDisplayWindowPolicyController =
+ displayManagerInternal.getDisplayWindowPolicyController(mDisplayId);
}
updateBaseDisplayMetrics(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
@@ -2821,8 +2827,14 @@
mBaseDisplayDensity = baseDensity;
if (mMaxUiWidth > 0 && mBaseDisplayWidth > mMaxUiWidth) {
- mBaseDisplayHeight = (mMaxUiWidth * mBaseDisplayHeight) / mBaseDisplayWidth;
+ final float ratio = mMaxUiWidth / (float) mBaseDisplayWidth;
+ mBaseDisplayHeight = (int) (mBaseDisplayHeight * ratio);
mBaseDisplayWidth = mMaxUiWidth;
+ if (!mIsDensityForced) {
+ // Update the density proportionally so the size of the UI elements won't change
+ // from the user's perspective.
+ mBaseDisplayDensity = (int) (mBaseDisplayDensity * ratio);
+ }
if (DEBUG_DISPLAY) {
Slog.v(TAG_WM, "Applying config restraints:" + mBaseDisplayWidth + "x"
@@ -2879,6 +2891,13 @@
/** If the given width and height equal to initial size, the setting will be cleared. */
void setForcedSize(int width, int height) {
+ // Can't force size higher than the maximal allowed
+ if (mMaxUiWidth > 0 && width > mMaxUiWidth) {
+ final float ratio = mMaxUiWidth / (float) width;
+ height = (int) (height * ratio);
+ width = mMaxUiWidth;
+ }
+
mIsSizeForced = mInitialDisplayWidth != width || mInitialDisplayHeight != height;
if (mIsSizeForced) {
// Set some sort of reasonable bounds on the size of the display that we will try
@@ -3420,6 +3439,10 @@
mInputMonitor.dump(pw, " ");
pw.println();
mInsetsStateController.dump(prefix, pw);
+ if (mDisplayWindowPolicyController != null) {
+ pw.println();
+ mDisplayWindowPolicyController.dump(prefix, pw);
+ }
}
@Override
@@ -4272,7 +4295,7 @@
boolean subtle) {
final WindowManagerPolicy policy = mWmService.mPolicy;
forAllWindows(w -> {
- if (w.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(w)
+ if (w.mActivityRecord == null && w.canBeHiddenByKeyguard()
&& w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
w.startAnimation(policy.createHiddenByKeyguardExit(
onWallpaper, goingToShade, subtle));
@@ -4643,12 +4666,9 @@
mWmService.requestTraversal();
}
+ @Override
boolean okToDisplay() {
- return okToDisplay(false);
- }
-
- boolean okToDisplay(boolean ignoreFrozen) {
- return okToDisplay(ignoreFrozen, false /* ignoreScreenOn */);
+ return okToDisplay(false /* ignoreFrozen */, false /* ignoreScreenOn */);
}
boolean okToDisplay(boolean ignoreFrozen, boolean ignoreScreenOn) {
@@ -4660,18 +4680,12 @@
return mDisplayInfo.state == Display.STATE_ON;
}
- boolean okToAnimate() {
- return okToAnimate(false);
- }
-
- boolean okToAnimate(boolean ignoreFrozen) {
- return okToAnimate(ignoreFrozen, false /* ignoreScreenOn */);
- }
-
+ @Override
boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
return okToDisplay(ignoreFrozen, ignoreScreenOn)
&& (mDisplayId != DEFAULT_DISPLAY
- || mWmService.mPolicy.okToAnimate(ignoreScreenOn));
+ || mWmService.mPolicy.okToAnimate(ignoreScreenOn))
+ && getDisplayPolicy().isScreenOnFully();
}
static final class TaskForResizePointSearchResult implements Predicate<Task> {
@@ -4801,7 +4815,10 @@
// WindowState#applyImeWindowsIfNeeded} in case of any state mismatch.
return dc.mImeLayeringTarget != null
&& (!dc.getDefaultTaskDisplayArea().isSplitScreenModeActivated()
- || dc.mImeLayeringTarget.getTask() == null);
+ || dc.mImeLayeringTarget.getTask() == null)
+ // Make sure that the IME window won't be skipped to report that it has
+ // completed the orientation change.
+ && !dc.mWmService.mDisplayFrozen;
}
/** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -4922,6 +4939,12 @@
reconfigureDisplayLocked();
onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
+ // Attach the SystemUiContext to this DisplayContent the get latest configuration.
+ // Note that the SystemUiContext will be removed automatically if this DisplayContent
+ // is detached.
+ mWmService.mWindowContextListenerController.registerWindowContainerListener(
+ getDisplayUiContext().getWindowContextToken(), this, SYSTEM_UID,
+ INVALID_WINDOW_TYPE, null /* options */);
}
}
@@ -5456,29 +5479,44 @@
return mMetricsLogger;
}
+ void acquireScreenOffToken(boolean acquire) {
+ if (acquire) {
+ mOffTokenAcquirer.acquire(mDisplayId);
+ } else {
+ mOffTokenAcquirer.release(mDisplayId);
+ }
+ }
+
void onDisplayChanged() {
mDisplay.getRealSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+ final int lastDisplayState = mDisplayInfo.state;
updateDisplayInfo();
// The window policy is responsible for stopping activities on the default display.
final int displayId = mDisplay.getDisplayId();
+ final int displayState = mDisplayInfo.state;
if (displayId != DEFAULT_DISPLAY) {
- final int displayState = mDisplay.getState();
if (displayState == Display.STATE_OFF) {
- mOffTokenAcquirer.acquire(mDisplayId);
+ acquireScreenOffToken(true /* acquire */);
} else if (displayState == Display.STATE_ON) {
- mOffTokenAcquirer.release(mDisplayId);
+ acquireScreenOffToken(false /* acquire */);
}
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
"Display %d state is now (%d), so update layer mirroring?",
mDisplayId, displayState);
- if (mLastDisplayState != displayState) {
+ if (lastDisplayState != displayState) {
// If state is on due to surface being added, then start layer mirroring.
// If state is off due to surface being removed, then stop layer mirroring.
updateMirroring();
}
- mLastDisplayState = displayState;
+ }
+ // Dispatch pending Configuration to WindowContext if the associated display changes to
+ // un-suspended state from suspended.
+ if (isSuspendedState(lastDisplayState)
+ && !isSuspendedState(displayState) && displayState != STATE_UNKNOWN) {
+ mWmService.mWindowContextListenerController
+ .dispatchPendingConfigurationIfNeeded(mDisplayId);
}
mWmService.requestTraversal();
}
@@ -5890,6 +5928,13 @@
}
/**
+ * @return whether keyguard should always be unlocked for this display
+ */
+ boolean isKeyguardAlwaysUnlocked() {
+ return (mDisplayInfo.flags & Display.FLAG_ALWAYS_UNLOCKED) != 0;
+ }
+
+ /**
* @return whether AOD is showing on this display
*/
boolean isAodShowing() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 09de6b3..cbb9d5d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -426,7 +426,7 @@
: service.mContext.createDisplayContext(displayContent.getDisplay());
mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext
: service.mAtmService.mSystemThread
- .createSystemUiContext(displayContent.getDisplayId());
+ .getSystemUiContext(displayContent.getDisplayId());
mDisplayContent = displayContent;
mLock = service.getWindowManagerLock();
@@ -752,6 +752,10 @@
public void setAwake(boolean awake) {
mAwake = awake;
+ // The screen off token for non-default display is controlled by DisplayContent.
+ if (mDisplayContent.isDefaultDisplay) {
+ mDisplayContent.acquireScreenOffToken(!awake);
+ }
}
public boolean isAwake() {
@@ -1738,7 +1742,7 @@
* @param imeTarget The current IME target window.
*/
private void applyKeyguardPolicy(WindowState win, WindowState imeTarget) {
- if (mService.mPolicy.canBeHiddenByKeyguardLw(win)) {
+ if (win.canBeHiddenByKeyguard()) {
if (shouldBeHiddenByKeyguard(win, imeTarget)) {
win.hide(false /* doAnimation */, true /* requestAnim */);
} else {
@@ -1767,7 +1771,7 @@
// Show IME over the keyguard if the target allows it.
final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisible()
&& win.mIsImWindow && (imeTarget.canShowWhenLocked()
- || !mService.mPolicy.canBeHiddenByKeyguardLw(imeTarget));
+ || !imeTarget.canBeHiddenByKeyguard());
if (showImeOverKeyguard) {
return false;
}
@@ -1887,7 +1891,8 @@
// user's package info (see ContextImpl.createDisplayContext)
final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo(
uiContext.getPackageName(), null, 0, userId);
- mCurrentUserResources = ResourcesManager.getInstance().getResources(null,
+ mCurrentUserResources = ResourcesManager.getInstance().getResources(
+ uiContext.getWindowContextToken(),
pi.getResDir(),
null /* splitResDirs */,
pi.getOverlayDirs(),
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index badb1f5..963f326 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -139,9 +139,6 @@
setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
}
}
- if (mTaskFragment.mTransitionController.isShellTransitionsEnabled()) {
- mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
- }
}
private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 83fd3fa..4225f21 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -50,7 +50,6 @@
import static java.lang.Integer.MAX_VALUE;
import android.annotation.Nullable;
-import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
import android.os.IBinder;
@@ -262,9 +261,6 @@
&& !mDisableWallpaperTouchEvents;
inputWindowHandle.setHasWallpaper(hasWallpaper);
- final Rect frame = w.getFrame();
- inputWindowHandle.setFrame(frame.left, frame.top, frame.right, frame.bottom);
-
// Surface insets are hardcoded to be the same in all directions
// and we could probably deprecate the "left/right/top/bottom" concept.
// we avoid reintroducing this concept by just choosing one of them here.
@@ -274,11 +270,19 @@
// what is on screen to what is actually being touched in the UI.
inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f);
- final int flags = w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs.flags);
- inputWindowHandle.setTouchableRegion(mTmpRegion);
+ // Update layout params flags to force the window to be not touch modal. We do this to
+ // restrict the window's touchable region to the task even if it request touches outside its
+ // window bounds. An example is a dialog in primary split should get touches outside its
+ // window within the primary task but should not get any touches going to the secondary
+ // task.
+ int flags = w.mAttrs.flags;
+ if (w.mAttrs.isModal()) {
+ flags = flags | FLAG_NOT_TOUCH_MODAL;
+ }
inputWindowHandle.setLayoutParamsFlags(flags);
- boolean useSurfaceCrop = false;
+ boolean useSurfaceBoundsAsTouchRegion = false;
+ SurfaceControl touchableRegionCrop = null;
final Task task = w.getTask();
if (task != null) {
// TODO(b/165794636): Remove the special case for freeform window once drag resizing is
@@ -290,20 +294,22 @@
// we need to make sure that these changes in crop are reflected in the input
// windows, and so ensure this flag is set so that the input crop always reflects
// the surface hierarchy.
- // TODO(b/168252846): we have some issues with modal-windows, so we need to cross
- // that bridge now that we organize full-screen Tasks.
- inputWindowHandle.setTouchableRegionCrop(null /* Use this surfaces crop */);
- inputWindowHandle.setReplaceTouchableRegionWithCrop(true);
- useSurfaceCrop = true;
+ useSurfaceBoundsAsTouchRegion = true;
+
+ if (w.mAttrs.isModal()) {
+ TaskFragment parent = w.getTaskFragment();
+ touchableRegionCrop = parent != null ? parent.getSurfaceControl() : null;
+ }
} else if (task.cropWindowsToRootTaskBounds() && !w.inFreeformWindowingMode()) {
- inputWindowHandle.setTouchableRegionCrop(task.getRootTask().getSurfaceControl());
- inputWindowHandle.setReplaceTouchableRegionWithCrop(false);
- useSurfaceCrop = true;
+ touchableRegionCrop = task.getRootTask().getSurfaceControl();
}
}
- if (!useSurfaceCrop) {
- inputWindowHandle.setReplaceTouchableRegionWithCrop(false);
- inputWindowHandle.setTouchableRegionCrop(null);
+ inputWindowHandle.setReplaceTouchableRegionWithCrop(useSurfaceBoundsAsTouchRegion);
+ inputWindowHandle.setTouchableRegionCrop(touchableRegionCrop);
+
+ if (!useSurfaceBoundsAsTouchRegion) {
+ w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs);
+ inputWindowHandle.setTouchableRegion(mTmpRegion);
}
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index fee9884..cabe414 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -75,14 +75,14 @@
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
private RootWindowContainer mRootWindowContainer;
- private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
+ private final ActivityTaskManagerService.SleepTokenAcquirer mSleepTokenAcquirer;
KeyguardController(ActivityTaskManagerService service,
ActivityTaskSupervisor taskSupervisor) {
mService = service;
mTaskSupervisor = taskSupervisor;
- mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
+ mSleepTokenAcquirer = mService.new SleepTokenAcquirer(KEYGUARD_SLEEP_TOKEN_TAG);
}
void setWindowManager(WindowManagerService windowManager) {
@@ -157,6 +157,11 @@
* Update the Keyguard showing state.
*/
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;
// If keyguard is going away, but SystemUI aborted the transition, need to reset state.
@@ -513,10 +518,10 @@
private boolean mRequestDismissKeyguard;
private final ActivityTaskManagerService mService;
- private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
+ private final ActivityTaskManagerService.SleepTokenAcquirer mSleepTokenAcquirer;
KeyguardDisplayState(ActivityTaskManagerService service, int displayId,
- ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) {
+ ActivityTaskManagerService.SleepTokenAcquirer acquirer) {
mService = service;
mDisplayId = displayId;
mSleepTokenAcquirer = acquirer;
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index 7c35a21..49d30cd 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -108,7 +108,7 @@
final WindowManagerPolicy policy = service.mPolicy;
service.mRoot.forAllWindows(nonAppWindow -> {
// Animation on the IME window is controlled via Insets.
- if (nonAppWindow.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(nonAppWindow)
+ if (nonAppWindow.mActivityRecord == null && nonAppWindow.canBeHiddenByKeyguard()
&& nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()
&& nonAppWindow != service.mRoot.getCurrentInputMethodWindow()) {
final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter(
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 3de98f1..7a7fb65 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -169,21 +169,35 @@
}
}
+ /**
+ * Returns true when the app specific configuration is successfully stored or removed based on
+ * the current requested configuration. It will return false when the requested
+ * configuration is same as the pre-existing app-specific configuration.
+ */
@GuardedBy("mLock")
boolean updateFromImpl(String packageName, int userId,
PackageConfigurationUpdaterImpl impl) {
synchronized (mLock) {
- PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId);
- if (impl.getNightMode() != null) {
- record.mNightMode = impl.getNightMode();
+ boolean isRecordPresent = false;
+ PackageConfigRecord record = findRecord(mModified, packageName, userId);
+ if (record != null) {
+ isRecordPresent = true;
+ } else {
+ record = findRecordOrCreate(mModified, packageName, userId);
}
- if (impl.getLocales() != null) {
- record.mLocales = impl.getLocales();
- }
+ boolean isNightModeChanged = updateNightMode(impl.getNightMode(), record);
+ boolean isLocalesChanged = updateLocales(impl.getLocales(), record);
+
if ((record.mNightMode == null || record.isResetNightMode())
&& (record.mLocales == null || record.mLocales.isEmpty())) {
// if all values default to system settings, we can remove the package.
removePackage(packageName, userId);
+ // if there was a pre-existing record for the package that was deleted,
+ // we return true (since it was successfully deleted), else false (since there was
+ // no change to the previous state).
+ return isRecordPresent;
+ } else if (!isNightModeChanged && !isLocalesChanged) {
+ return false;
} else {
final PackageConfigRecord pendingRecord =
findRecord(mPendingWrite, record.mName, record.mUserId);
@@ -195,7 +209,8 @@
writeRecord = pendingRecord;
}
- if (!updateNightMode(record, writeRecord) && !updateLocales(record, writeRecord)) {
+ if (!updateNightMode(record.mNightMode, writeRecord)
+ && !updateLocales(record.mLocales, writeRecord)) {
return false;
}
@@ -203,24 +218,24 @@
Slog.d(TAG, "PackageConfigUpdater save config " + writeRecord);
}
mPersisterQueue.addItem(new WriteProcessItem(writeRecord), false /* flush */);
+ return true;
}
- return true;
}
}
- private boolean updateNightMode(PackageConfigRecord record, PackageConfigRecord writeRecord) {
- if (record.mNightMode == null || record.mNightMode.equals(writeRecord.mNightMode)) {
+ private boolean updateNightMode(Integer requestedNightMode, PackageConfigRecord record) {
+ if (requestedNightMode == null || requestedNightMode.equals(record.mNightMode)) {
return false;
}
- writeRecord.mNightMode = record.mNightMode;
+ record.mNightMode = requestedNightMode;
return true;
}
- private boolean updateLocales(PackageConfigRecord record, PackageConfigRecord writeRecord) {
- if (record.mLocales == null || record.mLocales.equals(writeRecord.mLocales)) {
+ private boolean updateLocales(LocaleList requestedLocaleList, PackageConfigRecord record) {
+ if (requestedLocaleList == null || requestedLocaleList.equals(record.mLocales)) {
return false;
}
- writeRecord.mLocales = record.mLocales;
+ record.mLocales = requestedLocaleList;
return true;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ccee458..117b22a 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -225,7 +225,7 @@
private static final String DISPLAY_OFF_SLEEP_TOKEN_TAG = "Display-off";
/** The token acquirer to put root tasks on the displays to sleep */
- final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer;
+ final ActivityTaskManagerService.SleepTokenAcquirer mDisplayOffTokenAcquirer;
/**
* The modes which affect which tasks are returned when calling
@@ -470,7 +470,7 @@
mService = service.mAtmService;
mTaskSupervisor = mService.mTaskSupervisor;
mTaskSupervisor.mRootWindowContainer = this;
- mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG);
+ mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirer(DISPLAY_OFF_SLEEP_TOKEN_TAG);
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -2496,7 +2496,7 @@
// starts. Instead, we expect home activities to be launched when the system is ready
// (ActivityManagerService#systemReady).
if (mService.isBooted() || mService.isBooting()) {
- startSystemDecorations(display.mDisplayContent);
+ startSystemDecorations(display);
}
// Drop any cached DisplayInfos associated with this display id - the values are now
// out of date given this display added event.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bfb1a8e..b328e4d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2152,10 +2152,7 @@
}
private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
- if (mWmService.mDisableTransitionAnimation
- || !isVisible()
- || getSurfaceControl() == null
- || !isLeafTask()) {
+ if (!isLeafTask() || !canStartChangeTransition()) {
return false;
}
// Only do an animation into and out-of freeform mode for now. Other mode
@@ -3395,6 +3392,9 @@
info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
info.topActivityType = top.getActivityType();
info.isResizeable = isResizeable();
+ info.minWidth = mMinWidth;
+ info.minHeight = mMinHeight;
+ info.defaultMinSize = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
info.positionInParent = getRelativePosition();
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 600545e..796a90a 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -808,6 +808,10 @@
// Place root home tasks to the bottom.
layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer);
layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer);
+ // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
+ // make app pair split only have single root then we can just attach the
+ // divider to the single root task in shell.
+ layer = Math.max(layer, SPLIT_DIVIDER_LAYER + 1);
adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer);
t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
}
@@ -905,18 +909,24 @@
mBackgroundColor = colorInt;
Color color = Color.valueOf(colorInt);
mColorLayerCounter++;
- getPendingTransaction()
- .setColor(mSurfaceControl, new float[]{color.red(), color.green(), color.blue()});
- scheduleAnimation();
+ // Only apply the background color if the TDA is actually attached and has a valid surface
+ // to set the background color on. We still want to keep track of the background color state
+ // even if we are not showing it for when/if the TDA is reattached and gets a valid surface
+ if (mSurfaceControl != null) {
+ getPendingTransaction()
+ .setColor(mSurfaceControl,
+ new float[]{color.red(), color.green(), color.blue()});
+ scheduleAnimation();
+ }
}
void clearBackgroundColor() {
mColorLayerCounter--;
// Only clear the color layer if we have received the same amounts of clear as set
- // requests.
- if (mColorLayerCounter == 0) {
+ // requests and TDA has a non null surface control (i.e. is attached)
+ if (mColorLayerCounter == 0 && mSurfaceControl != null) {
getPendingTransaction().unsetColor(mSurfaceControl);
scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 929f221..e497b53 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2121,13 +2121,7 @@
/** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
private boolean shouldStartChangeTransition(Rect startBounds) {
- if (mWmService.mDisableTransitionAnimation
- || mDisplayContent == null
- || mTaskFragmentOrganizer == null
- || getSurfaceControl() == null
- // The change transition will be covered by display.
- || mDisplayContent.inTransition()
- || !isVisible()) {
+ if (mTaskFragmentOrganizer == null || !canStartChangeTransition()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 6d83fb6..29c27f9 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -31,10 +31,8 @@
import android.util.ArrayMap;
import android.util.Slog;
import android.view.RemoteAnimationDefinition;
-import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
import com.android.internal.protolog.common.ProtoLog;
@@ -135,11 +133,8 @@
void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
- final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(),
- "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared");
try {
- organizer.onTaskFragmentAppeared(
- new TaskFragmentAppearedInfo(info, outSurfaceControl));
+ organizer.onTaskFragmentAppeared(info);
mLastSentTaskFragmentInfos.put(tf, info);
tf.mTaskFragmentAppearedSent = true;
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f175eec..7349594 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -244,11 +244,11 @@
}
mParticipants.add(wc);
if (info.mShowWallpaper) {
- // Collect the wallpaper so it is part of the sync set.
- final WindowContainer wallpaper =
+ // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
+ final WindowState wallpaper =
wc.getDisplayContent().mWallpaperController.getTopVisibleWallpaper();
if (wallpaper != null) {
- collect(wallpaper);
+ collect(wallpaper.mToken);
}
}
}
@@ -495,25 +495,35 @@
Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
return;
}
- int displayId = DEFAULT_DISPLAY;
- for (WindowContainer container : mParticipants) {
- if (container.mDisplayContent == null) continue;
- displayId = container.mDisplayContent.getDisplayId();
+ boolean hasWallpaper = false;
+ DisplayContent dc = null;
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final WindowContainer<?> wc = mParticipants.valueAt(i);
+ if (dc == null && wc.mDisplayContent != null) {
+ dc = wc.mDisplayContent;
+ }
+ if (!hasWallpaper && isWallpaper(wc)) {
+ hasWallpaper = true;
+ }
}
+ if (dc == null) dc = mController.mAtm.mRootWindowContainer.getDefaultDisplay();
if (mState == STATE_ABORT) {
mController.abort(this);
- mController.mAtm.mRootWindowContainer.getDisplayContent(displayId)
- .getPendingTransaction().merge(transaction);
+ dc.getPendingTransaction().merge(transaction);
mSyncId = -1;
mOverrideOptions = null;
return;
}
+ // Ensure that wallpaper visibility is updated with the latest wallpaper target.
+ if (hasWallpaper) {
+ dc.mWallpaperController.adjustWallpaperWindows();
+ }
mState = STATE_PLAYING;
mController.moveToPlaying(this);
- if (mController.mAtm.mTaskSupervisor.getKeyguardController().isKeyguardLocked(displayId)) {
+ if (dc.isKeyguardLocked()) {
mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED;
}
@@ -523,9 +533,9 @@
info.setAnimationOptions(mOverrideOptions);
// TODO(b/188669821): Move to animation impl in shell.
- handleLegacyRecentsStartBehavior(displayId, info);
+ handleLegacyRecentsStartBehavior(dc, info);
- handleNonAppWindowsInTransition(displayId, mType, mFlags);
+ handleNonAppWindowsInTransition(dc, mType, mFlags);
reportStartReasonsToLogger();
@@ -627,14 +637,11 @@
}
/** @see RecentsAnimationController#attachNavigationBarToApp */
- private void handleLegacyRecentsStartBehavior(int displayId, TransitionInfo info) {
+ private void handleLegacyRecentsStartBehavior(DisplayContent dc, TransitionInfo info) {
if ((mFlags & TRANSIT_FLAG_IS_RECENTS) == 0) {
return;
}
- final DisplayContent dc =
- mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
- if (dc == null) return;
- mRecentsDisplayId = displayId;
+ mRecentsDisplayId = dc.mDisplayId;
// Recents has an input-consumer to grab input from the "live tile" app. Set that up here
final InputConsumerImpl recentsAnimationInputConsumer =
@@ -679,7 +686,7 @@
// Find the top-most non-home, closing app.
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change c = info.getChanges().get(i);
- if (c.getTaskInfo() == null || c.getTaskInfo().displayId != displayId
+ if (c.getTaskInfo() == null || c.getTaskInfo().displayId != mRecentsDisplayId
|| c.getTaskInfo().getActivityType() != ACTIVITY_TYPE_STANDARD
|| !(c.getMode() == TRANSIT_CLOSE || c.getMode() == TRANSIT_TO_BACK)) {
continue;
@@ -710,7 +717,7 @@
t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
}
if (mController.mStatusBar != null) {
- mController.mStatusBar.setNavigationBarLumaSamplingEnabled(displayId, false);
+ mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, false);
}
}
@@ -760,13 +767,8 @@
}
}
- private void handleNonAppWindowsInTransition(int displayId,
+ private void handleNonAppWindowsInTransition(@NonNull DisplayContent dc,
@TransitionType int transit, @TransitionFlags int flags) {
- final DisplayContent dc =
- mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
- if (dc == null) {
- return;
- }
if ((transit == TRANSIT_KEYGUARD_GOING_AWAY
|| (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0)
&& !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 08b1a2f..e24be37 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -611,8 +611,9 @@
private void updateWallpaperTokens(boolean visible) {
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.updateWallpaperWindows(visible);
- token.getDisplayContent().assignWindowLayers(false);
+ if (token.updateWallpaperWindows(visible)) {
+ token.mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 3a639f5..fe405e5 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -104,18 +104,21 @@
}
}
- void updateWallpaperWindows(boolean visible) {
+ /** Returns {@code true} if visibility is changed. */
+ boolean updateWallpaperWindows(boolean visible) {
+ boolean changed = false;
if (isVisible() != visible) {
ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
token, visible);
setVisibility(visible);
+ changed = true;
}
- final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
if (mTransitionController.isShellTransitionsEnabled()) {
- return;
+ return changed;
}
- final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
+ final WindowState wallpaperTarget =
+ mDisplayContent.mWallpaperController.getWallpaperTarget();
if (visible && wallpaperTarget != null) {
final RecentsAnimationController recentsAnimationController =
@@ -137,6 +140,7 @@
}
setVisible(visible);
+ return changed;
}
private void setVisible(boolean visible) {
@@ -155,10 +159,12 @@
* transition. In that situation, make sure to call {@link #commitVisibility} when done.
*/
void setVisibility(boolean visible) {
- // Before setting mVisibleRequested so we can track changes.
- mTransitionController.collect(this);
+ if (mVisibleRequested != visible) {
+ // Before setting mVisibleRequested so we can track changes.
+ mTransitionController.collect(this);
- setVisibleRequested(visible);
+ setVisibleRequested(visible);
+ }
// If in a transition, defer commits for activities that are going invisible
if (!visible && (mTransitionController.inTransition()
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 9865506..7e84dbb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2543,6 +2543,13 @@
mSurfaceFreezer.unfreeze(getPendingTransaction());
}
+ /** Whether we can start change transition with this window and current display status. */
+ boolean canStartChangeTransition() {
+ return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
+ && getSurfaceControl() != null && !mDisplayContent.inTransition()
+ && isVisible() && isVisibleRequested() && okToAnimate();
+ }
+
/**
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
*
@@ -2891,12 +2898,7 @@
}
boolean okToAnimate() {
- return okToAnimate(false /* ignoreFrozen */);
- }
-
- boolean okToAnimate(boolean ignoreFrozen) {
- final DisplayContent dc = getDisplayContent();
- return dc != null && dc.okToAnimate(ignoreFrozen);
+ return okToAnimate(false /* ignoreFrozen */, false /* ignoreScreenOn */);
}
boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index bc53041..cc52713 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.isSuspendedState;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.window.WindowProviderService.isWindowProviderService;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
@@ -45,7 +47,7 @@
*
* <ul>
* <li>When a {@link WindowContext} is created, it registers the listener via
- * {@link WindowManagerService#registerWindowContextListener(IBinder, int, int, Bundle)}
+ * {@link WindowManagerService#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)}
* automatically.</li>
* <li>When the {@link WindowContext} adds the first window to the screen via
* {@link android.view.WindowManager#addView(View, android.view.ViewGroup.LayoutParams)},
@@ -53,7 +55,7 @@
* to corresponding {@link WindowToken} via this controller.</li>
* <li>When the {@link WindowContext} is GCed, it unregisters the previously
* registered listener via
- * {@link WindowManagerService#unregisterWindowContextListener(IBinder)}.
+ * {@link WindowManagerService#detachWindowContextFromWindowContainer(IBinder)}.
* {@link WindowManagerService} is also responsible for removing the
* {@link WindowContext} created {@link WindowToken}.</li>
* </ul>
@@ -68,7 +70,7 @@
/**
* Registers the listener to a {@code container} which is associated with
- * a {@code clientToken}, which is a {@link android.app.WindowContext} representation. If the
+ * a {@code clientToken}, which is a {@link android.window.WindowContext} representation. If the
* listener associated with {@code clientToken} hasn't been initialized yet, create one
* {@link WindowContextListenerImpl}. Otherwise, the listener associated with
* {@code clientToken} switches to listen to the {@code container}.
@@ -80,7 +82,7 @@
* @param options a bundle used to pass window-related options.
*/
void registerWindowContainerListener(@NonNull IBinder clientToken,
- @NonNull WindowContainer container, int ownerUid, @WindowType int type,
+ @NonNull WindowContainer<?> container, int ownerUid, @WindowType int type,
@Nullable Bundle options) {
WindowContextListenerImpl listener = mListeners.get(clientToken);
if (listener == null) {
@@ -103,6 +105,16 @@
listener.unregister();
}
+ void dispatchPendingConfigurationIfNeeded(int displayId) {
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ final WindowContextListenerImpl listener = mListeners.valueAt(i);
+ if (listener.getWindowContainer().getDisplayContent().getDisplayId() == displayId
+ && listener.mHasPendingConfiguration) {
+ listener.reportConfigToWindowTokenClient();
+ }
+ }
+ }
+
/**
* Verifies if the caller is allowed to do the operation to the listener specified by
* {@code clientToken}.
@@ -138,7 +150,7 @@
return listener != null ? listener.mOptions : null;
}
- @Nullable WindowContainer getContainer(IBinder clientToken) {
+ @Nullable WindowContainer<?> getContainer(IBinder clientToken) {
final WindowContextListenerImpl listener = mListeners.get(clientToken);
return listener != null ? listener.mContainer : null;
}
@@ -163,7 +175,7 @@
class WindowContextListenerImpl implements WindowContainerListener {
@NonNull private final IBinder mClientToken;
private final int mOwnerUid;
- @NonNull private WindowContainer mContainer;
+ @NonNull private WindowContainer<?> mContainer;
/**
* The options from {@link Context#createWindowContext(int, Bundle)}.
* <p>It can be used for choosing the {@link DisplayArea} where the window context
@@ -177,7 +189,9 @@
private int mLastReportedDisplay = INVALID_DISPLAY;
private Configuration mLastReportedConfig;
- private WindowContextListenerImpl(IBinder clientToken, WindowContainer container,
+ private boolean mHasPendingConfiguration;
+
+ private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container,
int ownerUid, @WindowType int type, @Nullable Bundle options) {
mClientToken = clientToken;
mContainer = Objects.requireNonNull(container);
@@ -197,11 +211,11 @@
/** TEST ONLY: returns the {@link WindowContainer} of the listener */
@VisibleForTesting
- WindowContainer getWindowContainer() {
+ WindowContainer<?> getWindowContainer() {
return mContainer;
}
- private void updateContainer(@NonNull WindowContainer newContainer) {
+ private void updateContainer(@NonNull WindowContainer<?> newContainer) {
Objects.requireNonNull(newContainer);
if (mContainer.equals(newContainer)) {
@@ -246,12 +260,20 @@
if (mDeathRecipient == null) {
throw new IllegalStateException("Invalid client token: " + mClientToken);
}
-
- if (mLastReportedConfig == null) {
- mLastReportedConfig = new Configuration();
+ // If the display of window context associated window container is suspended, don't
+ // report the configuration update. Note that we still dispatch the configuration update
+ // to WindowProviderService to make it compatible with Service#onConfigurationChanged.
+ // Service always receives #onConfigurationChanged callback regardless of display state.
+ if (!isWindowProviderService(mOptions)
+ && isSuspendedState(mContainer.getDisplayContent().getDisplayInfo().state)) {
+ mHasPendingConfiguration = true;
+ return;
}
final Configuration config = mContainer.getConfiguration();
final int displayId = mContainer.getDisplayContent().getDisplayId();
+ if (mLastReportedConfig == null) {
+ mLastReportedConfig = new Configuration();
+ }
if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
// No changes since last reported time.
return;
@@ -266,6 +288,7 @@
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
}
+ mHasPendingConfiguration = false;
}
@Override
@@ -283,7 +306,7 @@
// If we cannot obtain the DisplayContent, the DisplayContent may also be removed.
// We should proceed the removal process.
if (dc != null) {
- final DisplayArea da = dc.findAreaForToken(windowToken);
+ final DisplayArea<?> da = dc.findAreaForToken(windowToken);
updateContainer(da);
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4720d7c..23f9c58 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2700,6 +2700,9 @@
@Override
public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
type, int displayId, Bundle options) {
+ if (clientToken == null) {
+ throw new IllegalArgumentException("clientToken must not be null!");
+ }
final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
"attachWindowContextToDisplayArea", false /* printLog */);
final int callingUid = Binder.getCallingUid();
@@ -2790,6 +2793,39 @@
}
}
+ @Override
+ public Configuration attachToDisplayContent(IBinder clientToken, int displayId) {
+ if (clientToken == null) {
+ throw new IllegalArgumentException("clientToken must not be null!");
+ }
+ final int callingUid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ // We use "getDisplayContent" instead of "getDisplayContentOrCreate" because
+ // this method may be called in DisplayPolicy's constructor and may cause
+ // infinite loop. In this scenario, we early return here and switch to do the
+ // registration in DisplayContent#onParentChanged at DisplayContent initialization.
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ if (Binder.getCallingPid() != myPid()) {
+ throw new WindowManager.InvalidDisplayException("attachToDisplayContent: "
+ + "trying to attach to a non-existing display:" + displayId);
+ }
+ // Early return if this method is invoked from system process.
+ // See above comments for more detail.
+ return null;
+ }
+
+ mWindowContextListenerController.registerWindowContainerListener(clientToken, dc,
+ callingUid, INVALID_WINDOW_TYPE, null /* options */);
+ return dc.getConfiguration();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
/** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 3d479d1..0649b25 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -587,7 +587,7 @@
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
final TaskFragmentCreationParams taskFragmentCreationOptions =
hop.getTaskFragmentCreationOptions();
- createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
+ createTaskFragment(taskFragmentCreationOptions, errorCallbackToken, caller);
break;
}
case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
@@ -630,7 +630,7 @@
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
final int result = mService.getActivityStartController()
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
- hop.getCallingActivity());
+ hop.getCallingActivity(), caller.mUid, caller.mPid);
if (!isStartResultSuccessful(result)) {
sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
errorCallbackToken,
@@ -1199,7 +1199,7 @@
}
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
- @Nullable IBinder errorCallbackToken) {
+ @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
final ActivityRecord ownerActivity =
ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface(
@@ -1217,9 +1217,9 @@
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
- // The ownerActivity has to belong to the same app as the root Activity of the target Task.
- final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
- if (rootActivity.getUid() != ownerActivity.getUid()) {
+ // The ownerActivity has to belong to the same app as the target Task.
+ if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
+ || ownerActivity.getTask().effectiveUid != caller.mUid) {
final Throwable exception =
new IllegalArgumentException("Not allowed to operate with the ownerToken while "
+ "the root activity of the target task belong to the different app");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 81b241e..bae5465 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -48,7 +48,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -1338,8 +1337,7 @@
outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height());
}
- @Override
- public WindowManager.LayoutParams getAttrs() {
+ WindowManager.LayoutParams getAttrs() {
return mAttrs;
}
@@ -2687,10 +2685,9 @@
}
}
- int getSurfaceTouchableRegion(Region region, int flags) {
- final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+ void getSurfaceTouchableRegion(Region region, WindowManager.LayoutParams attrs) {
+ final boolean modal = attrs.isModal();
if (modal) {
- flags |= FLAG_NOT_TOUCH_MODAL;
if (mActivityRecord != null) {
// Limit the outer touch to the activity root task region.
updateRegionForModalActivityWindow(region);
@@ -2722,8 +2719,6 @@
if (mInvGlobalScale != 1.f) {
region.scale(mInvGlobalScale);
}
-
- return flags;
}
/**
@@ -3414,7 +3409,10 @@
}
// Exclude toast because legacy apps may show toast window by themselves, so the misused
// apps won't always be considered as foreground state.
- if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST) {
+ // Exclude private presentations as they can only be shown on private virtual displays and
+ // shouldn't be the cause of an app be considered foreground.
+ if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST
+ && mAttrs.type != TYPE_PRIVATE_PRESENTATION) {
mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
}
}
@@ -3559,10 +3557,9 @@
* {@link WindowManager.LayoutParams#FLAG_NOT_TOUCH_MODAL touch modality.}
*/
void getEffectiveTouchableRegion(Region outRegion) {
- final boolean modal = (mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
final DisplayContent dc = getDisplayContent();
- if (modal && dc != null) {
+ if (mAttrs.isModal() && dc != null) {
outRegion.set(dc.getBounds());
cropRegionToRootTaskBoundsIfNeeded(outRegion);
subtractTouchExcludeRegionIfNeeded(outRegion);
@@ -3835,6 +3832,24 @@
return (mAttrs.insetsFlags.behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
}
+ boolean canBeHiddenByKeyguard() {
+ // Keyguard visibility of window from activities are determined over activity visibility.
+ if (mActivityRecord != null) {
+ return false;
+ }
+ switch (mAttrs.type) {
+ case TYPE_NOTIFICATION_SHADE:
+ case TYPE_STATUS_BAR:
+ case TYPE_NAVIGATION_BAR:
+ case TYPE_WALLPAPER:
+ return false;
+ default:
+ // Hide only windows below the keyguard host window.
+ return mPolicy.getWindowLayerLw(this)
+ < mPolicy.getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
+ }
+ }
+
private int getRootTaskId() {
final Task rootTask = getRootTask();
if (rootTask == null) {
@@ -4710,6 +4725,48 @@
return false;
}
+ private boolean shouldFinishAnimatingExit() {
+ // Exit animation might be applied soon.
+ if (inTransition()) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isTransition: %s",
+ this);
+ return false;
+ }
+ if (!mDisplayContent.okToAnimate()) {
+ return true;
+ }
+ // Exit animation is running.
+ if (isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES)) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isAnimating: %s",
+ this);
+ return false;
+ }
+ // If the wallpaper is currently behind this app window, we need to change both of
+ // them inside of a transaction to avoid artifacts.
+ if (mDisplayContent.mWallpaperController.isWallpaperTarget(this)) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
+ "shouldWaitAnimatingExit: isWallpaperTarget: %s", this);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * If this is window is stuck in the animatingExit status, resume clean up procedure blocked
+ * by the exit animation.
+ */
+ void cleanupAnimatingExitWindow() {
+ // TODO(b/205335975): WindowManagerService#tryStartExitingAnimation starts an exit animation
+ // and set #mAnimationExit. After the exit animation finishes, #onExitAnimationDone shall
+ // be called, but there seems to be a case that #onExitAnimationDone is not triggered, so
+ // a windows stuck in the animatingExit status.
+ if (mAnimatingExit && shouldFinishAnimatingExit()) {
+ ProtoLog.w(WM_DEBUG_APP_TRANSITIONS, "Clear window stuck on animatingExit status: %s",
+ this);
+ onExitAnimationDone();
+ }
+ }
+
void onExitAnimationDone() {
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 6204824..ca834c5 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -48,12 +48,12 @@
class WindowTracing {
/**
- * Maximum buffer size, currently defined as 512 KB
+ * Maximum buffer size, currently defined as 5 MB
* Size was experimentally defined to fit between 100 to 150 elements.
*/
- private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024;
- private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024;
- private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
+ private static final int BUFFER_CAPACITY_CRITICAL = 5120 * 1024; // 5 MB
+ private static final int BUFFER_CAPACITY_TRIM = 10240 * 1024; // 10 MB
+ private static final int BUFFER_CAPACITY_ALL = 20480 * 1024; // 20 MB
static final String WINSCOPE_EXT = ".winscope";
private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace" + WINSCOPE_EXT;
private static final String TAG = "WindowTracing";
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 0a55003..2ccef9a 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -29,6 +29,8 @@
#include <android/hardware/gnss/2.1/IGnssMeasurement.h>
#include <android/hardware/gnss/BnGnss.h>
#include <android/hardware/gnss/BnGnssCallback.h>
+#include <android/hardware/gnss/BnGnssGeofence.h>
+#include <android/hardware/gnss/BnGnssGeofenceCallback.h>
#include <android/hardware/gnss/BnGnssMeasurementCallback.h>
#include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
#include <android/hardware/gnss/BnGnssPsdsCallback.h>
@@ -54,13 +56,13 @@
#include "gnss/GnssBatching.h"
#include "gnss/GnssConfiguration.h"
#include "gnss/GnssMeasurement.h"
+#include "gnss/GnssNavigationMessage.h"
#include "gnss/Utils.h"
#include "hardware_legacy/power.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
-static jclass class_gnssNavigationMessage;
static jclass class_gnssPowerStats;
static jmethodID method_reportLocation;
@@ -83,7 +85,6 @@
static jmethodID method_reportGeofenceRemoveStatus;
static jmethodID method_reportGeofencePauseStatus;
static jmethodID method_reportGeofenceResumeStatus;
-static jmethodID method_reportNavigationMessages;
static jmethodID method_reportGnssServiceDied;
static jmethodID method_reportGnssPowerStats;
static jmethodID method_setSubHalMeasurementCorrectionsCapabilities;
@@ -113,7 +114,6 @@
static jmethodID method_correctionPlaneAzimDeg;
static jmethodID method_reportNfwNotification;
static jmethodID method_isInEmergencySession;
-static jmethodID method_gnssNavigationMessageCtor;
static jmethodID method_gnssPowerStatsCtor;
static jmethodID method_setSubHalPowerIndicationCapabilities;
@@ -191,6 +191,9 @@
using android::hardware::gnss::PsdsType;
using IGnssAidl = android::hardware::gnss::IGnss;
using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
+using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
+using IGnssGeofenceAidl = android::hardware::gnss::IGnssGeofence;
+using IGnssGeofenceCallbackAidl = android::hardware::gnss::IGnssGeofenceCallback;
using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
@@ -216,6 +219,8 @@
sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
sp<IGnssAidl> gnssHalAidl = nullptr;
+sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr;
+sp<IGnssGeofenceAidl> gnssGeofenceAidlIface = nullptr;
sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
@@ -226,7 +231,6 @@
sp<IGnssDebug_V1_0> gnssDebugIface = nullptr;
sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr;
sp<IGnssNi> gnssNiIface = nullptr;
-sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr;
sp<IGnssPowerIndication> gnssPowerIndicationIface = nullptr;
sp<IMeasurementCorrections_V1_0> gnssCorrectionsIface_V1_0 = nullptr;
sp<IMeasurementCorrections_V1_1> gnssCorrectionsIface_V1_1 = nullptr;
@@ -235,6 +239,7 @@
std::unique_ptr<GnssConfigurationInterface> gnssConfigurationIface = nullptr;
std::unique_ptr<android::gnss::GnssMeasurementInterface> gnssMeasurementIface = nullptr;
+std::unique_ptr<android::gnss::GnssNavigationMessageInterface> gnssNavigationMessageIface = nullptr;
std::unique_ptr<android::gnss::GnssBatchingInterface> gnssBatchingIface = nullptr;
#define WAKE_LOCK_NAME "GPS"
@@ -709,35 +714,25 @@
return Void();
}
-/*
- * GnssGeofenceCallback class implements the callback methods for the
- * IGnssGeofence interface.
- */
-struct GnssGeofenceCallback : public IGnssGeofenceCallback {
- // Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
- Return<void> gnssGeofenceTransitionCb(
- int32_t geofenceId,
- const GnssLocation_V1_0& location,
- GeofenceTransition transition,
- hardware::gnss::V1_0::GnssUtcTime timestamp) override;
- Return<void>
- gnssGeofenceStatusCb(
- GeofenceAvailability status,
- const GnssLocation_V1_0& location) override;
- Return<void> gnssGeofenceAddCb(int32_t geofenceId,
- GeofenceStatus status) override;
- Return<void> gnssGeofenceRemoveCb(int32_t geofenceId,
- GeofenceStatus status) override;
- Return<void> gnssGeofencePauseCb(int32_t geofenceId,
- GeofenceStatus status) override;
- Return<void> gnssGeofenceResumeCb(int32_t geofenceId,
- GeofenceStatus status) override;
+/** Util class for GnssGeofenceCallback methods. */
+struct GnssGeofenceCallbackUtil {
+ template <class T>
+ static void gnssGeofenceTransitionCb(int geofenceId, const T& location, int transition,
+ int64_t timestampMillis);
+ template <class T>
+ static void gnssGeofenceStatusCb(int availability, const T& lastLocation);
+ static void gnssGeofenceAddCb(int geofenceId, int status);
+ static void gnssGeofenceRemoveCb(int geofenceId, int status);
+ static void gnssGeofencePauseCb(int geofenceId, int status);
+ static void gnssGeofenceResumeCb(int geofenceId, int status);
+
+private:
+ GnssGeofenceCallbackUtil() = delete;
};
-Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
- int32_t geofenceId, const GnssLocation_V1_0& location,
- GeofenceTransition transition,
- hardware::gnss::V1_0::GnssUtcTime timestamp) {
+template <class T>
+void GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(int geofenceId, const T& location,
+ int transition, int64_t timestamp) {
JNIEnv* env = getJniEnv();
jobject jLocation = translateGnssLocation(env, location);
@@ -751,27 +746,22 @@
checkAndClearExceptionFromCallback(env, __FUNCTION__);
env->DeleteLocalRef(jLocation);
- return Void();
}
-Return<void>
-GnssGeofenceCallback::gnssGeofenceStatusCb(GeofenceAvailability status,
- const GnssLocation_V1_0& location) {
+template <class T>
+void GnssGeofenceCallbackUtil::gnssGeofenceStatusCb(int availability, const T& lastLocation) {
JNIEnv* env = getJniEnv();
- jobject jLocation = translateGnssLocation(env, location);
+ jobject jLocation = translateGnssLocation(env, lastLocation);
- env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status,
- jLocation);
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, availability, jLocation);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
env->DeleteLocalRef(jLocation);
- return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId,
- GeofenceStatus status) {
+void GnssGeofenceCallbackUtil::gnssGeofenceAddCb(int geofenceId, int status) {
JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
+ if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
ALOGE("%s: Error in adding a Geofence: %d\n", __func__, status);
}
@@ -780,13 +770,11 @@
geofenceId,
status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId,
- GeofenceStatus status) {
+void GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(int geofenceId, int status) {
JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
+ if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
ALOGE("%s: Error in removing a Geofence: %d\n", __func__, status);
}
@@ -794,13 +782,11 @@
method_reportGeofenceRemoveStatus,
geofenceId, status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId,
- GeofenceStatus status) {
+void GnssGeofenceCallbackUtil::gnssGeofencePauseCb(int geofenceId, int status) {
JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
+ if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
ALOGE("%s: Error in pausing Geofence: %d\n", __func__, status);
}
@@ -808,13 +794,11 @@
method_reportGeofencePauseStatus,
geofenceId, status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId,
- GeofenceStatus status) {
+void GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(int geofenceId, int status) {
JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
+ if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
ALOGE("%s: Error in resuming Geofence: %d\n", __func__, status);
}
@@ -822,50 +806,104 @@
method_reportGeofenceResumeStatus,
geofenceId, status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
}
/*
- * GnssNavigationMessageCallback interface implements the callback methods
- * required by the IGnssNavigationMessage interface.
+ * GnssGeofenceCallbackAidl class implements the callback methods for the IGnssGeofence AIDL
+ * interface.
*/
-struct GnssNavigationMessageCallback : public IGnssNavigationMessageCallback {
- /*
- * Methods from ::android::hardware::gps::V1_0::IGnssNavigationMessageCallback
- * follow.
- */
- Return<void> gnssNavigationMessageCb(
- const IGnssNavigationMessageCallback::GnssNavigationMessage& message) override;
+struct GnssGeofenceCallbackAidl : public android::hardware::gnss::BnGnssGeofenceCallback {
+ Status gnssGeofenceTransitionCb(int geofenceId, const GnssLocationAidl& location,
+ int transition, int64_t timestampMillis) override;
+ Status gnssGeofenceStatusCb(int availability, const GnssLocationAidl& lastLocation) override;
+ Status gnssGeofenceAddCb(int geofenceId, int status) override;
+ Status gnssGeofenceRemoveCb(int geofenceId, int status) override;
+ Status gnssGeofencePauseCb(int geofenceId, int status) override;
+ Status gnssGeofenceResumeCb(int geofenceId, int status) override;
};
-Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb(
- const IGnssNavigationMessageCallback::GnssNavigationMessage& message) {
- JNIEnv* env = getJniEnv();
+Status GnssGeofenceCallbackAidl::gnssGeofenceTransitionCb(int geofenceId,
+ const GnssLocationAidl& location,
+ int transition, int64_t timestampMillis) {
+ GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(geofenceId, location, transition,
+ timestampMillis);
+ return Status::ok();
+}
- size_t dataLength = message.data.size();
+Status GnssGeofenceCallbackAidl::gnssGeofenceStatusCb(int availability,
+ const GnssLocationAidl& lastLocation) {
+ GnssGeofenceCallbackUtil::gnssGeofenceStatusCb(availability, lastLocation);
+ return Status::ok();
+}
- std::vector<uint8_t> navigationData = message.data;
- uint8_t* data = &(navigationData[0]);
- if (dataLength == 0 || data == nullptr) {
- ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data,
- dataLength);
- return Void();
- }
+Status GnssGeofenceCallbackAidl::gnssGeofenceAddCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceAddCb(geofenceId, status);
+ return Status::ok();
+}
- JavaObject object(env, class_gnssNavigationMessage, method_gnssNavigationMessageCtor);
- SET(Type, static_cast<int32_t>(message.type));
- SET(Svid, static_cast<int32_t>(message.svid));
- SET(MessageId, static_cast<int32_t>(message.messageId));
- SET(SubmessageId, static_cast<int32_t>(message.submessageId));
- object.callSetter("setData", data, dataLength);
- SET(Status, static_cast<int32_t>(message.status));
+Status GnssGeofenceCallbackAidl::gnssGeofenceRemoveCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(geofenceId, status);
+ return Status::ok();
+}
- jobject navigationMessage = object.get();
- env->CallVoidMethod(mCallbacksObj,
- method_reportNavigationMessages,
- navigationMessage);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- env->DeleteLocalRef(navigationMessage);
+Status GnssGeofenceCallbackAidl::gnssGeofencePauseCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofencePauseCb(geofenceId, status);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceResumeCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(geofenceId, status);
+ return Status::ok();
+}
+
+/*
+ * GnssGeofenceCallback class implements the callback methods for the
+ * IGnssGeofence HIDL interface.
+ */
+struct GnssGeofenceCallback : public IGnssGeofenceCallback {
+ // Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
+ Return<void> gnssGeofenceTransitionCb(int32_t geofenceId, const GnssLocation_V1_0& location,
+ GeofenceTransition transition,
+ hardware::gnss::V1_0::GnssUtcTime timestamp) override;
+ Return<void> gnssGeofenceStatusCb(GeofenceAvailability status,
+ const GnssLocation_V1_0& location) override;
+ Return<void> gnssGeofenceAddCb(int32_t geofenceId, GeofenceStatus status) override;
+ Return<void> gnssGeofenceRemoveCb(int32_t geofenceId, GeofenceStatus status) override;
+ Return<void> gnssGeofencePauseCb(int32_t geofenceId, GeofenceStatus status) override;
+ Return<void> gnssGeofenceResumeCb(int32_t geofenceId, GeofenceStatus status) override;
+};
+
+Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
+ int32_t geofenceId, const GnssLocation_V1_0& location, GeofenceTransition transition,
+ hardware::gnss::V1_0::GnssUtcTime timestamp) {
+ GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(geofenceId, location, (int)transition,
+ (int64_t)timestamp);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(GeofenceAvailability availability,
+ const GnssLocation_V1_0& location) {
+ GnssGeofenceCallbackUtil::gnssGeofenceStatusCb((int)availability, location);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId, GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceAddCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId, GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId, GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofencePauseCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId, GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(geofenceId, (int)status);
return Void();
}
@@ -1180,10 +1218,6 @@
"(II)V");
method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus",
"(II)V");
- method_reportNavigationMessages = env->GetMethodID(
- clazz,
- "reportNavigationMessage",
- "(Landroid/location/GnssNavigationMessage;)V");
method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification",
"(Ljava/lang/String;BLjava/lang/String;BLjava/lang/String;BZZ)V");
@@ -1253,14 +1287,11 @@
class_gnssPowerStats = (jclass)env->NewGlobalRef(gnssPowerStatsClass);
method_gnssPowerStatsCtor = env->GetMethodID(class_gnssPowerStats, "<init>", "(IJDDDDDD[D)V");
- jclass gnssNavigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
- class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass);
- method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V");
-
gnss::GnssAntennaInfo_class_init_once(env, clazz);
gnss::GnssBatching_class_init_once(env, clazz);
gnss::GnssConfiguration_class_init_once(env);
gnss::GnssMeasurement_class_init_once(env, clazz);
+ gnss::GnssNavigationMessage_class_init_once(env, clazz);
gnss::Utils_class_init_once(env);
}
@@ -1279,11 +1310,13 @@
android_location_gnss_hal_GnssNative_set_gps_service_handle();
}
- if (gnssHal == nullptr) {
+ if (gnssHal == nullptr && gnssHalAidl == nullptr) {
ALOGE("Unable to get GPS service\n");
return;
}
+ // TODO: linkToDeath for AIDL HAL
+
gnssHalDeathRecipient = new GnssDeathRecipient();
hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
if (!linked.isOk()) {
@@ -1303,7 +1336,7 @@
} else {
ALOGD("Unable to get a handle to PSDS AIDL interface.");
}
- } else {
+ } else if (gnssHal != nullptr) {
auto gnssXtra = gnssHal->getExtensionXtra();
if (!gnssXtra.isOk()) {
ALOGD("Unable to get a handle to Xtra");
@@ -1320,7 +1353,7 @@
agnssRilIface_V2_0 = agnssRil_V2_0;
agnssRilIface = agnssRilIface_V2_0;
}
- } else {
+ } else if (gnssHal != nullptr) {
auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
if (!agnssRil_V1_0.isOk()) {
ALOGD("Unable to get a handle to AGnssRil");
@@ -1336,7 +1369,7 @@
} else {
agnssIface_V2_0 = agnss_V2_0;
}
- } else {
+ } else if (gnssHal != nullptr) {
auto agnss_V1_0 = gnssHal->getExtensionAGnss();
if (!agnss_V1_0.isOk()) {
ALOGD("Unable to get a handle to AGnss");
@@ -1345,11 +1378,21 @@
}
}
- auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
- if (!gnssNavigationMessage.isOk()) {
- ALOGD("Unable to get a handle to GnssNavigationMessage");
- } else {
- gnssNavigationMessageIface = gnssNavigationMessage;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<hardware::gnss::IGnssNavigationMessageInterface> gnssNavigationMessage;
+ auto status = gnssHalAidl->getExtensionGnssNavigationMessage(&gnssNavigationMessage);
+ if (checkAidlStatus(status,
+ "Unable to get a handle to GnssNavigationMessage AIDL interface.")) {
+ gnssNavigationMessageIface =
+ std::make_unique<gnss::GnssNavigationMessageAidl>(gnssNavigationMessage);
+ }
+ } else if (gnssHal != nullptr) {
+ auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
+ if (checkHidlReturn(gnssNavigationMessage,
+ "Unable to get a handle to GnssNavigationMessage interface.")) {
+ gnssNavigationMessageIface =
+ std::make_unique<gnss::GnssNavigationMessageHidl>(gnssNavigationMessage);
+ }
}
// Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means,
@@ -1387,12 +1430,12 @@
std::make_unique<android::gnss::GnssMeasurement_V1_1>(gnssMeasurement);
}
}
- if (gnssMeasurementIface == nullptr) {
- auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
- if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) {
- gnssMeasurementIface =
- std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement);
- }
+ if (gnssHal != nullptr && gnssMeasurementIface == nullptr) {
+ auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
+ if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) {
+ gnssMeasurementIface =
+ std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement);
+ }
}
if (gnssHal_V2_1 != nullptr) {
@@ -1434,7 +1477,7 @@
gnssDebugIface = gnssDebugIface_V2_0;
}
}
- if (gnssDebugIface == nullptr) {
+ if (gnssHal != nullptr && gnssDebugIface == nullptr) {
auto gnssDebug = gnssHal->getExtensionGnssDebug();
if (!gnssDebug.isOk()) {
ALOGD("Unable to get a handle to GnssDebug");
@@ -1443,11 +1486,13 @@
}
}
- auto gnssNi = gnssHal->getExtensionGnssNi();
- if (!gnssNi.isOk()) {
- ALOGD("Unable to get a handle to GnssNi");
- } else {
- gnssNiIface = gnssNi;
+ if (gnssHal != nullptr) {
+ auto gnssNi = gnssHal->getExtensionGnssNi();
+ if (!gnssNi.isOk()) {
+ ALOGD("Unable to get a handle to GnssNi");
+ } else {
+ gnssNiIface = gnssNi;
+ }
}
if (gnssHalAidl != nullptr) {
@@ -1488,11 +1533,17 @@
}
}
- auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
- if (!gnssGeofencing.isOk()) {
- ALOGD("Unable to get a handle to GnssGeofencing");
- } else {
- gnssGeofencingIface = gnssGeofencing;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<IGnssGeofenceAidl> gnssGeofenceAidl;
+ auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofenceAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence interface.")) {
+ gnssGeofenceAidlIface = gnssGeofenceAidl;
+ }
+ } else if (gnssHal != nullptr) {
+ auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
+ if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) {
+ gnssGeofencingIface = gnssGeofencing;
+ }
}
if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
@@ -1507,7 +1558,7 @@
gnssBatchingIface = std::make_unique<gnss::GnssBatching_V2_0>(gnssBatching_V2_0);
}
}
- if (gnssBatchingIface == nullptr) {
+ if (gnssHal != nullptr && gnssBatchingIface == nullptr) {
auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching();
if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) {
gnssBatchingIface = std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0);
@@ -1568,7 +1619,7 @@
/*
* Fail if the main interface fails to initialize
*/
- if (gnssHal == nullptr) {
+ if (gnssHal == nullptr && gnssHalAidl == nullptr) {
ALOGE("Unable to initialize GNSS HAL.");
return JNI_FALSE;
}
@@ -1583,7 +1634,7 @@
result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
} else if (gnssHal_V1_1 != nullptr) {
result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
- } else {
+ } else if (gnssHal != nullptr) {
result = gnssHal->setCallback(gnssCbIface);
}
@@ -1630,10 +1681,18 @@
}
// Set IGnssGeofencing.hal callback.
- sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
- if (gnssGeofencingIface != nullptr) {
+ if (gnssGeofenceAidlIface != nullptr) {
+ sp<IGnssGeofenceCallbackAidl> gnssGeofenceCallbackAidl = new GnssGeofenceCallbackAidl();
+ auto status = gnssGeofenceAidlIface->setCallback(gnssGeofenceCallbackAidl);
+ if (!checkAidlStatus(status, "IGnssGeofenceAidl setCallback() failed.")) {
+ gnssGeofenceAidlIface = nullptr;
+ }
+ } else if (gnssGeofencingIface != nullptr) {
+ sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
auto status = gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
- checkHidlReturn(status, "IGnssGeofencing setCallback() failed.");
+ if (!checkHidlReturn(status, "IGnssGeofencing setCallback() failed.")) {
+ gnssGeofencingIface = nullptr;
+ }
} else {
ALOGI("Unable to initialize IGnssGeofencing interface.");
}
@@ -1693,12 +1752,15 @@
}
static void android_location_gnss_hal_GnssNative_cleanup(JNIEnv* /* env */, jclass) {
- if (gnssHal == nullptr) {
- return;
+ if (gnssHalAidl != nullptr) {
+ auto status = gnssHalAidl->close();
+ checkAidlStatus(status, "IGnssAidl close() failed.");
}
- auto result = gnssHal->cleanup();
- checkHidlReturn(result, "IGnss cleanup() failed.");
+ if (gnssHal != nullptr) {
+ auto result = gnssHal->cleanup();
+ checkHidlReturn(result, "IGnss cleanup() failed.");
+ }
}
static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
@@ -2177,57 +2239,85 @@
static jboolean android_location_gnss_hal_GnssNative_is_geofence_supported(JNIEnv* /* env */,
jclass) {
- return (gnssGeofencingIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+ if (gnssGeofencingIface == nullptr && gnssGeofenceAidlIface == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
}
static jboolean android_location_gnss_hal_GnssNative_add_geofence(
JNIEnv* /* env */, jclass, jint geofenceId, jdouble latitude, jdouble longitude,
jdouble radius, jint last_transition, jint monitor_transition,
jint notification_responsiveness, jint unknown_timer) {
- if (gnssGeofencingIface == nullptr) {
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ if (gnssGeofenceAidlIface != nullptr) {
+ auto status =
+ gnssGeofenceAidlIface->addGeofence(geofenceId, latitude, longitude, radius,
+ last_transition, monitor_transition,
+ notification_responsiveness, unknown_timer);
+ return checkAidlStatus(status, "IGnssGeofenceAidl addGeofence() failed.");
}
- auto result = gnssGeofencingIface->addGeofence(
- geofenceId, latitude, longitude, radius,
- static_cast<IGnssGeofenceCallback::GeofenceTransition>(last_transition),
- monitor_transition, notification_responsiveness, unknown_timer);
- return checkHidlReturn(result, "IGnssGeofencing addGeofence() failed.");
+ if (gnssGeofencingIface != nullptr) {
+ auto result = gnssGeofencingIface
+ ->addGeofence(geofenceId, latitude, longitude, radius,
+ static_cast<IGnssGeofenceCallback::GeofenceTransition>(
+ last_transition),
+ monitor_transition, notification_responsiveness,
+ unknown_timer);
+ return checkHidlReturn(result, "IGnssGeofencing addGeofence() failed.");
+ }
+
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
static jboolean android_location_gnss_hal_GnssNative_remove_geofence(JNIEnv* /* env */, jclass,
jint geofenceId) {
- if (gnssGeofencingIface == nullptr) {
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ if (gnssGeofenceAidlIface != nullptr) {
+ auto status = gnssGeofenceAidlIface->removeGeofence(geofenceId);
+ return checkAidlStatus(status, "IGnssGeofenceAidl removeGeofence() failed.");
}
- auto result = gnssGeofencingIface->removeGeofence(geofenceId);
- return checkHidlReturn(result, "IGnssGeofencing removeGeofence() failed.");
+ if (gnssGeofencingIface != nullptr) {
+ auto result = gnssGeofencingIface->removeGeofence(geofenceId);
+ return checkHidlReturn(result, "IGnssGeofencing removeGeofence() failed.");
+ }
+
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
static jboolean android_location_gnss_hal_GnssNative_pause_geofence(JNIEnv* /* env */, jclass,
jint geofenceId) {
- if (gnssGeofencingIface == nullptr) {
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ if (gnssGeofenceAidlIface != nullptr) {
+ auto status = gnssGeofenceAidlIface->pauseGeofence(geofenceId);
+ return checkAidlStatus(status, "IGnssGeofenceAidl pauseGeofence() failed.");
}
- auto result = gnssGeofencingIface->pauseGeofence(geofenceId);
- return checkHidlReturn(result, "IGnssGeofencing pauseGeofence() failed.");
+ if (gnssGeofencingIface != nullptr) {
+ auto result = gnssGeofencingIface->pauseGeofence(geofenceId);
+ return checkHidlReturn(result, "IGnssGeofencing pauseGeofence() failed.");
+ }
+
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
static jboolean android_location_gnss_hal_GnssNative_resume_geofence(JNIEnv* /* env */, jclass,
jint geofenceId,
jint monitor_transition) {
- if (gnssGeofencingIface == nullptr) {
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ if (gnssGeofenceAidlIface != nullptr) {
+ auto status = gnssGeofenceAidlIface->resumeGeofence(geofenceId, monitor_transition);
+ return checkAidlStatus(status, "IGnssGeofenceAidl resumeGeofence() failed.");
}
- auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
- return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
+ if (gnssGeofencingIface != nullptr) {
+ auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
+ return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
+ }
+
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
static jboolean android_location_gnss_hal_GnssNative_is_antenna_info_supported(JNIEnv* env,
@@ -2489,20 +2579,8 @@
return JNI_FALSE;
}
- sp<IGnssNavigationMessageCallback> gnssNavigationMessageCbIface =
- new GnssNavigationMessageCallback();
- auto result = gnssNavigationMessageIface->setCallback(gnssNavigationMessageCbIface);
- if (!checkHidlReturn(result, "IGnssNavigationMessage setCallback() failed.")) {
- return JNI_FALSE;
- }
-
- IGnssNavigationMessage::GnssNavigationMessageStatus initRet = result;
- if (initRet != IGnssNavigationMessage::GnssNavigationMessageStatus::SUCCESS) {
- ALOGE("An error has been found in %s: %d", __FUNCTION__, static_cast<int32_t>(initRet));
- return JNI_FALSE;
- }
-
- return JNI_TRUE;
+ return gnssNavigationMessageIface->setCallback(
+ std::make_unique<gnss::GnssNavigationMessageCallback>());
}
static jboolean android_location_gnss_hal_GnssNative_stop_navigation_message_collection(JNIEnv* env,
@@ -2511,9 +2589,7 @@
ALOGE("%s: IGnssNavigationMessage interface not available.", __func__);
return JNI_FALSE;
}
-
- auto result = gnssNavigationMessageIface->close();
- return checkHidlReturn(result, "IGnssNavigationMessage close() failed.");
+ return gnssNavigationMessageIface->close();
}
static jboolean android_location_GnssConfiguration_set_emergency_supl_pdn(JNIEnv*,
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index 090166a..6c6b304 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -29,6 +29,8 @@
"GnssConfiguration.cpp",
"GnssMeasurement.cpp",
"GnssMeasurementCallback.cpp",
+ "GnssNavigationMessage.cpp",
+ "GnssNavigationMessageCallback.cpp",
"Utils.cpp",
],
}
diff --git a/services/core/jni/gnss/GnssNavigationMessage.cpp b/services/core/jni/gnss/GnssNavigationMessage.cpp
new file mode 100644
index 0000000..75aee74
--- /dev/null
+++ b/services/core/jni/gnss/GnssNavigationMessage.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssNavigationMessageJni"
+
+#include "GnssNavigationMessage.h"
+
+#include "Utils.h"
+
+namespace android::gnss {
+
+using hardware::gnss::IGnssNavigationMessageInterface;
+using IGnssNavigationMessageHidl = hardware::gnss::V1_0::IGnssNavigationMessage;
+
+// Implementation of GnssNavigationMessage (AIDL HAL)
+
+GnssNavigationMessageAidl::GnssNavigationMessageAidl(
+ const sp<IGnssNavigationMessageInterface>& iGnssNavigationMessage)
+ : mIGnssNavigationMessage(iGnssNavigationMessage) {
+ assert(mIGnssNavigationMessage != nullptr);
+}
+
+jboolean GnssNavigationMessageAidl::setCallback(
+ const std::unique_ptr<GnssNavigationMessageCallback>& callback) {
+ auto status = mIGnssNavigationMessage->setCallback(callback->getAidl());
+ return checkAidlStatus(status, "IGnssNavigationMessageAidl setCallback() failed.");
+}
+
+jboolean GnssNavigationMessageAidl::close() {
+ auto status = mIGnssNavigationMessage->close();
+ return checkAidlStatus(status, "IGnssNavigationMessageAidl close() failed");
+}
+
+// Implementation of GnssNavigationMessageHidl
+
+GnssNavigationMessageHidl::GnssNavigationMessageHidl(
+ const sp<IGnssNavigationMessageHidl>& iGnssNavigationMessage)
+ : mIGnssNavigationMessageHidl(iGnssNavigationMessage) {
+ assert(mIGnssNavigationMessageHidl != nullptr);
+}
+
+jboolean GnssNavigationMessageHidl::setCallback(
+ const std::unique_ptr<GnssNavigationMessageCallback>& callback) {
+ auto result = mIGnssNavigationMessageHidl->setCallback(callback->getHidl());
+
+ IGnssNavigationMessageHidl::GnssNavigationMessageStatus initRet = result;
+ if (initRet != IGnssNavigationMessageHidl::GnssNavigationMessageStatus::SUCCESS) {
+ ALOGE("An error has been found in %s: %d", __FUNCTION__, static_cast<int32_t>(initRet));
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+jboolean GnssNavigationMessageHidl::close() {
+ auto result = mIGnssNavigationMessageHidl->close();
+ return checkHidlReturn(result, "IGnssNavigationMessage close() failed.");
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssNavigationMessage.h b/services/core/jni/gnss/GnssNavigationMessage.h
new file mode 100644
index 0000000..e3a1e4a
--- /dev/null
+++ b/services/core/jni/gnss/GnssNavigationMessage.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSNAVIGATIONMESSAGE_H
+#define _ANDROID_SERVER_GNSS_GNSSNAVIGATIONMESSAGE_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssNavigationMessage.h>
+#include <android/hardware/gnss/BnGnssNavigationMessageInterface.h>
+#include <log/log.h>
+
+#include "GnssNavigationMessageCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+class GnssNavigationMessageInterface {
+public:
+ virtual ~GnssNavigationMessageInterface() {}
+ virtual jboolean setCallback(
+ const std::unique_ptr<GnssNavigationMessageCallback>& callback) = 0;
+ virtual jboolean close() = 0;
+};
+
+class GnssNavigationMessageAidl : public GnssNavigationMessageInterface {
+public:
+ GnssNavigationMessageAidl(const sp<android::hardware::gnss::IGnssNavigationMessageInterface>&
+ iGnssNavigationMessage);
+ jboolean setCallback(const std::unique_ptr<GnssNavigationMessageCallback>& callback) override;
+ jboolean close() override;
+
+private:
+ const sp<android::hardware::gnss::IGnssNavigationMessageInterface> mIGnssNavigationMessage;
+};
+
+class GnssNavigationMessageHidl : public GnssNavigationMessageInterface {
+public:
+ GnssNavigationMessageHidl(const sp<android::hardware::gnss::V1_0::IGnssNavigationMessage>&
+ iGnssNavigationMessage);
+ jboolean setCallback(const std::unique_ptr<GnssNavigationMessageCallback>& callback) override;
+ jboolean close() override;
+
+private:
+ const sp<android::hardware::gnss::V1_0::IGnssNavigationMessage> mIGnssNavigationMessageHidl;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSNAVIGATIONMESSAGE_H
diff --git a/services/core/jni/gnss/GnssNavigationMessageCallback.cpp b/services/core/jni/gnss/GnssNavigationMessageCallback.cpp
new file mode 100644
index 0000000..1779c95
--- /dev/null
+++ b/services/core/jni/gnss/GnssNavigationMessageCallback.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "GnssNavMsgCbJni"
+
+#include "GnssNavigationMessageCallback.h"
+
+namespace android::gnss {
+
+namespace {
+
+jclass class_gnssNavigationMessage;
+jmethodID method_reportNavigationMessages;
+jmethodID method_gnssNavigationMessageCtor;
+
+} // anonymous namespace
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+
+using GnssNavigationMessageAidl =
+ android::hardware::gnss::IGnssNavigationMessageCallback::GnssNavigationMessage;
+using GnssNavigationMessageHidl =
+ android::hardware::gnss::V1_0::IGnssNavigationMessageCallback::GnssNavigationMessage;
+
+void GnssNavigationMessage_class_init_once(JNIEnv* env, jclass clazz) {
+ method_reportNavigationMessages =
+ env->GetMethodID(clazz, "reportNavigationMessage",
+ "(Landroid/location/GnssNavigationMessage;)V");
+
+ jclass gnssNavigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
+ class_gnssNavigationMessage = (jclass)env->NewGlobalRef(gnssNavigationMessageClass);
+ method_gnssNavigationMessageCtor =
+ env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V");
+}
+
+Status GnssNavigationMessageCallbackAidl::gnssNavigationMessageCb(
+ const GnssNavigationMessageAidl& message) {
+ GnssNavigationMessageCallbackUtil::gnssNavigationMessageCbImpl(message);
+ return Status::ok();
+}
+
+Return<void> GnssNavigationMessageCallbackHidl::gnssNavigationMessageCb(
+ const GnssNavigationMessageHidl& message) {
+ GnssNavigationMessageCallbackUtil::gnssNavigationMessageCbImpl(message);
+ return Void();
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssNavigationMessageCallback.h b/services/core/jni/gnss/GnssNavigationMessageCallback.h
new file mode 100644
index 0000000..fe76fc7
--- /dev/null
+++ b/services/core/jni/gnss/GnssNavigationMessageCallback.h
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSNAVIGATIONMESSAGECALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSNAVIGATIONMESSAGECALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssNavigationMessage.h>
+#include <android/hardware/gnss/BnGnssNavigationMessageCallback.h>
+#include <log/log.h>
+
+#include <vector>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+namespace {
+
+extern jclass class_gnssNavigationMessage;
+extern jmethodID method_reportNavigationMessages;
+extern jmethodID method_gnssNavigationMessageCtor;
+
+} // anonymous namespace
+
+void GnssNavigationMessage_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssNavigationMessageCallbackAidl : public hardware::gnss::BnGnssNavigationMessageCallback {
+public:
+ GnssNavigationMessageCallbackAidl() {}
+ android::binder::Status gnssNavigationMessageCb(
+ const hardware::gnss::IGnssNavigationMessageCallback::GnssNavigationMessage& message)
+ override;
+};
+
+class GnssNavigationMessageCallbackHidl
+ : public hardware::gnss::V1_0::IGnssNavigationMessageCallback {
+public:
+ GnssNavigationMessageCallbackHidl() {}
+
+ hardware::Return<void> gnssNavigationMessageCb(
+ const hardware::gnss::V1_0::IGnssNavigationMessageCallback::GnssNavigationMessage&
+ message) override;
+};
+
+class GnssNavigationMessageCallback {
+public:
+ GnssNavigationMessageCallback() {}
+ sp<GnssNavigationMessageCallbackAidl> getAidl() {
+ if (callbackAidl == nullptr) {
+ callbackAidl = sp<GnssNavigationMessageCallbackAidl>::make();
+ }
+ return callbackAidl;
+ }
+
+ sp<GnssNavigationMessageCallbackHidl> getHidl() {
+ if (callbackHidl == nullptr) {
+ callbackHidl = sp<GnssNavigationMessageCallbackHidl>::make();
+ }
+ return callbackHidl;
+ }
+
+private:
+ sp<GnssNavigationMessageCallbackAidl> callbackAidl;
+ sp<GnssNavigationMessageCallbackHidl> callbackHidl;
+};
+
+struct GnssNavigationMessageCallbackUtil {
+ template <class T>
+ static void gnssNavigationMessageCbImpl(const T& message);
+
+private:
+ GnssNavigationMessageCallbackUtil() = delete;
+};
+
+template <class T>
+void GnssNavigationMessageCallbackUtil::gnssNavigationMessageCbImpl(const T& message) {
+ JNIEnv* env = getJniEnv();
+
+ size_t dataLength = message.data.size();
+
+ std::vector<uint8_t> navigationData = message.data;
+ uint8_t* data = &(navigationData[0]);
+ if (dataLength == 0 || data == nullptr) {
+ ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
+ return;
+ }
+
+ JavaObject object(env, class_gnssNavigationMessage, method_gnssNavigationMessageCtor);
+ SET(Type, static_cast<int32_t>(message.type));
+ SET(Svid, static_cast<int32_t>(message.svid));
+ SET(MessageId, static_cast<int32_t>(message.messageId));
+ SET(SubmessageId, static_cast<int32_t>(message.submessageId));
+ object.callSetter("setData", data, dataLength);
+ SET(Status, static_cast<int32_t>(message.status));
+
+ jobject navigationMessage = object.get();
+ env->CallVoidMethod(mCallbacksObj, method_reportNavigationMessages, navigationMessage);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(navigationMessage);
+ return;
+}
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSNAVIGATIONMESSAGECALLBACK_H
\ No newline at end of file
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 429edf1..2f4dd57 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -26,6 +26,10 @@
<xs:element name="displayConfiguration">
<xs:complexType>
<xs:sequence>
+ <xs:element type="densityMap" name="densityMap" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element type="nitsMap" name="screenBrightnessMap">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
@@ -181,5 +185,27 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="densityMap">
+ <xs:sequence>
+ <xs:element name="density" type="density" maxOccurs="unbounded" minOccurs="1">
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="density">
+ <xs:sequence>
+ <xs:element type="xs:nonNegativeInteger" name="width">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="xs:nonNegativeInteger" name="height">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="xs:nonNegativeInteger" name="density">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </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 ad18602..5b2b87c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -1,8 +1,24 @@
// Signature format: 2.0
package com.android.server.display.config {
+ public class Density {
+ ctor public Density();
+ method @NonNull public final java.math.BigInteger getDensity();
+ method @NonNull public final java.math.BigInteger getHeight();
+ method @NonNull public final java.math.BigInteger getWidth();
+ method public final void setDensity(@NonNull java.math.BigInteger);
+ method public final void setHeight(@NonNull java.math.BigInteger);
+ method public final void setWidth(@NonNull java.math.BigInteger);
+ }
+
+ public class DensityMap {
+ ctor public DensityMap();
+ method public java.util.List<com.android.server.display.config.Density> getDensity();
+ }
+
public class DisplayConfiguration {
ctor public DisplayConfiguration();
+ method @Nullable public final com.android.server.display.config.DensityMap getDensityMap();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public final com.android.server.display.config.SensorDetails getProxSensor();
@@ -13,6 +29,7 @@
method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+ method public final void setDensityMap(@Nullable com.android.server.display.config.DensityMap);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 727f265..f605fe8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8460,7 +8460,9 @@
@Override
public boolean hasDeviceOwner() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ || canManageUsers(caller)
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return mOwners.hasDeviceOwner();
}
@@ -8640,7 +8642,8 @@
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
synchronized (getLockObject()) {
if (!mOwners.hasDeviceOwner()) {
@@ -8986,7 +8989,8 @@
return DevicePolicyManager.STATE_USER_UNMANAGED;
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
+ Preconditions.checkCallAuthorization(canManageUsers(caller)
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return getUserProvisioningState(caller.getUserId());
}
@@ -9240,7 +9244,8 @@
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return getProfileOwnerNameUnchecked(userHandle);
}
@@ -10226,7 +10231,8 @@
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.QUERY_ADMIN_POLICY));
synchronized (getLockObject()) {
List<String> result = null;
@@ -10394,14 +10400,24 @@
}
@Override
- public List<String> getPermittedInputMethodsForCurrentUser() {
+ public @Nullable List<String> getPermittedInputMethodsAsUser(@UserIdInt int userId) {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
+ Preconditions.checkCallAuthorization(canManageUsers(caller)
+ || hasCallingOrSelfPermission(permission.QUERY_ADMIN_POLICY));
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return getPermittedInputMethodsUnchecked(userId);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+ private @Nullable List<String> getPermittedInputMethodsUnchecked(@UserIdInt int userId) {
synchronized (getLockObject()) {
List<String> result = null;
// Only device or profile owners can have permitted lists set.
- List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(caller.getUserId());
+ List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(userId);
for (ActiveAdmin admin: admins) {
List<String> fromAdmin = admin.permittedInputMethods;
if (fromAdmin != null) {
@@ -10416,7 +10432,7 @@
// If we have a permitted list add all system input methods.
if (result != null) {
List<InputMethodInfo> imes = InputMethodManagerInternal
- .get().getInputMethodListAsUser(caller.getUserId());
+ .get().getInputMethodListAsUser(userId);
if (imes != null) {
for (InputMethodInfo ime : imes) {
ServiceInfo serviceInfo = ime.getServiceInfo();
@@ -10582,7 +10598,12 @@
return null;
}
}
- if (!mUserManager.canAddMoreUsers()) {
+
+ String userType = demo ? UserManager.USER_TYPE_FULL_DEMO
+ : UserManager.USER_TYPE_FULL_SECONDARY;
+ int userInfoFlags = ephemeral ? UserInfo.FLAG_EPHEMERAL : 0;
+
+ if (!mUserManager.canAddMoreUsers(userType)) {
if (targetSdkVersion >= Build.VERSION_CODES.P) {
throw new ServiceSpecificException(
UserManager.USER_OPERATION_ERROR_MAX_USERS, "user limit reached");
@@ -10591,9 +10612,6 @@
}
}
- int userInfoFlags = ephemeral ? UserInfo.FLAG_EPHEMERAL : 0;
- String userType = demo ? UserManager.USER_TYPE_FULL_DEMO
- : UserManager.USER_TYPE_FULL_SECONDARY;
String[] disallowedPackages = null;
if (!leaveAllSystemAppsEnabled) {
disallowedPackages = mOverlayPackagesProvider.getNonRequiredApps(admin,
@@ -16466,7 +16484,8 @@
if (!mHasFeature) {
return false;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
long id = mInjector.binderClearCallingIdentity();
try {
@@ -16492,7 +16511,8 @@
if (!mHasFeature) {
return false;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return mInjector.binderWithCleanCallingIdentity(() -> isUnattendedManagedKioskUnchecked());
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9119133..a72cf3a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -176,6 +176,7 @@
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.role.RoleServicePlatformHelper;
import com.android.server.rotationresolver.RotationResolverManagerService;
+import com.android.server.security.AttestationVerificationManagerService;
import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
@@ -373,6 +374,8 @@
"com.android.server.blob.BlobStoreManagerService";
private static final String APP_SEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.appsearch.AppSearchManagerService";
+ private static final String ISOLATED_COMPILATION_SERVICE_CLASS =
+ "com.android.server.compos.IsolatedCompilationService";
private static final String ROLLBACK_MANAGER_SERVICE_CLASS =
"com.android.server.rollback.RollbackManagerService";
private static final String ALARM_MANAGER_SERVICE_CLASS =
@@ -396,6 +399,8 @@
private static final String UWB_APEX_SERVICE_JAR_PATH =
"/apex/com.android.uwb/javalib/service-uwb.jar";
private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";
+ private static final String SAFETY_CENTER_SERVICE_CLASS =
+ "com.android.safetycenter.SafetyCenterService";
private static final String SUPPLEMENTALPROCESS_APEX_PATH =
"/apex/com.android.supplementalprocess/javalib/service-supplementalprocess.jar";
@@ -2335,6 +2340,10 @@
t.traceEnd();
}
+ t.traceBegin("StartAttestationVerificationService");
+ mSystemServiceManager.startService(AttestationVerificationManagerService.class);
+ t.traceEnd();
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
t.traceBegin("StartCompanionDeviceManager");
mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS);
@@ -2714,10 +2723,20 @@
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
t.traceEnd();
+ t.traceBegin("StartSafetyCenterService");
+ mSystemServiceManager.startService(SAFETY_CENTER_SERVICE_CLASS);
+ t.traceEnd();
+
t.traceBegin("AppSearchManagerService");
mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) {
+ t.traceBegin("IsolatedCompilationService");
+ mSystemServiceManager.startService(ISOLATED_COMPILATION_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
t.traceBegin("StartMediaCommunicationService");
mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
t.traceEnd();
diff --git a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
index 42115d4..b7f8c00 100644
--- a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
@@ -55,8 +55,8 @@
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.OnTransportRegisteredListener;
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportClientManager;
+import com.android.server.backup.transport.TransportConnection;
+import com.android.server.backup.transport.TransportConnectionManager;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
@@ -85,7 +85,7 @@
private static final String PACKAGE_B = "some.package.b";
@Mock private OnTransportRegisteredListener mListener;
- @Mock private TransportClientManager mTransportClientManager;
+ @Mock private TransportConnectionManager mTransportConnectionManager;
private TransportData mTransportA1;
private TransportData mTransportA2;
private TransportData mTransportB1;
@@ -206,7 +206,7 @@
transportManager.registerTransports();
- verify(mTransportClientManager)
+ verify(mTransportConnectionManager)
.getTransportClient(
eq(mTransportA1.getTransportComponent()),
argThat(
@@ -433,10 +433,10 @@
TransportManager transportManager =
createTransportManagerWithRegisteredTransports(mTransportA1, mTransportA2);
- TransportClient transportClient =
+ TransportConnection transportConnection =
transportManager.getTransportClient(mTransportA1.transportName, "caller");
- assertThat(transportClient.getTransportComponent())
+ assertThat(transportConnection.getTransportComponent())
.isEqualTo(mTransportA1.getTransportComponent());
}
@@ -453,10 +453,10 @@
null,
null);
- TransportClient transportClient =
+ TransportConnection transportConnection =
transportManager.getTransportClient(mTransportA1.transportName, "caller");
- assertThat(transportClient).isNull();
+ assertThat(transportConnection).isNull();
}
@Test
@@ -471,9 +471,10 @@
null,
null);
- TransportClient transportClient = transportManager.getTransportClient("newName", "caller");
+ TransportConnection transportConnection = transportManager.getTransportClient(
+ "newName", "caller");
- assertThat(transportClient.getTransportComponent())
+ assertThat(transportConnection.getTransportComponent())
.isEqualTo(mTransportA1.getTransportComponent());
}
@@ -482,9 +483,10 @@
TransportManager transportManager =
createTransportManagerWithRegisteredTransports(mTransportA1, mTransportA2);
- TransportClient transportClient = transportManager.getCurrentTransportClient("caller");
+ TransportConnection transportConnection = transportManager.getCurrentTransportClient(
+ "caller");
- assertThat(transportClient.getTransportComponent())
+ assertThat(transportConnection.getTransportComponent())
.isEqualTo(mTransportA1.getTransportComponent());
}
@@ -660,12 +662,12 @@
List<TransportMock> transportMocks = new ArrayList<>(transports.length);
for (TransportData transport : transports) {
TransportMock transportMock = mockTransport(transport);
- when(mTransportClientManager.getTransportClient(
+ when(mTransportConnectionManager.getTransportClient(
eq(transport.getTransportComponent()), any()))
- .thenReturn(transportMock.transportClient);
- when(mTransportClientManager.getTransportClient(
+ .thenReturn(transportMock.mTransportConnection);
+ when(mTransportConnectionManager.getTransportClient(
eq(transport.getTransportComponent()), any(), any()))
- .thenReturn(transportMock.transportClient);
+ .thenReturn(transportMock.mTransportConnection);
transportMocks.add(transportMock);
}
return transportMocks;
@@ -706,7 +708,7 @@
.map(TransportData::getTransportComponent)
.collect(toSet()),
selectedTransport != null ? selectedTransport.transportName : null,
- mTransportClientManager);
+ mTransportConnectionManager);
transportManager.setOnTransportRegisteredListener(mListener);
return transportManager;
}
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 06d51a4..297538a 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -243,7 +243,7 @@
assertThat(result).isTrue();
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
}
/**
@@ -282,7 +282,7 @@
assertThat(filtered).asList().containsExactly(PACKAGE_1);
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
}
/**
@@ -357,7 +357,7 @@
assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
verify(mTransportManager)
- .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ .disposeOfTransportClient(eq(mNewTransportMock.mTransportConnection), any());
}
/**
@@ -395,7 +395,7 @@
assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
verify(callback).onSuccess(eq(mNewTransport.transportName));
verify(mTransportManager)
- .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ .disposeOfTransportClient(eq(mNewTransportMock.mTransportConnection), any());
}
/**
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index a14cc51..bf4eeae 100644
--- a/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -50,7 +50,7 @@
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.testing.shadows.ShadowSlog;
import org.junit.Before;
@@ -285,7 +285,7 @@
TransportData transport = transportsIterator.next();
verify(mTransportManager).getTransportClient(eq(transport.transportName), any());
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
}
}
@@ -303,9 +303,9 @@
performInitializeTask.run();
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMocks.get(0).transportClient), any());
+ .disposeOfTransportClient(eq(transportMocks.get(0).mTransportConnection), any());
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMocks.get(1).transportClient), any());
+ .disposeOfTransportClient(eq(transportMocks.get(1).mTransportConnection), any());
}
@Test
@@ -328,14 +328,16 @@
setUpTransports(mTransportManager, transport1, transport2);
String registeredTransportName = transport2.transportName;
IBackupTransport registeredTransport = transportMocks.get(1).transport;
- TransportClient registeredTransportClient = transportMocks.get(1).transportClient;
+ TransportConnection
+ registeredTransportConnection = transportMocks.get(1).mTransportConnection;
PerformInitializeTask performInitializeTask =
createPerformInitializeTask(transport1.transportName, transport2.transportName);
performInitializeTask.run();
verify(registeredTransport).initializeDevice();
- verify(mTransportManager).disposeOfTransportClient(eq(registeredTransportClient), any());
+ verify(mTransportManager).disposeOfTransportClient(eq(registeredTransportConnection),
+ any());
verify(mObserver).onResult(eq(registeredTransportName), eq(TRANSPORT_OK));
}
@@ -347,7 +349,7 @@
performInitializeTask.run();
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
verify(mListener).onFinished(any());
}
@@ -356,13 +358,13 @@
public void testRun_whenTransportThrowsDeadObjectException() throws Exception {
TransportMock transportMock = setUpTransport(mTransport);
IBackupTransport transport = transportMock.transport;
- TransportClient transportClient = transportMock.transportClient;
+ TransportConnection transportConnection = transportMock.mTransportConnection;
when(transport.initializeDevice()).thenThrow(DeadObjectException.class);
PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransportManager).disposeOfTransportClient(eq(transportClient), any());
+ verify(mTransportManager).disposeOfTransportClient(eq(transportConnection), any());
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
verify(mListener).onFinished(any());
}
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 7d17109..fd295c0 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -2665,7 +2665,7 @@
KeyValueBackupTask task =
new KeyValueBackupTask(
mBackupManagerService,
- transportMock.transportClient,
+ transportMock.mTransportConnection,
transportMock.transportData.transportDirName,
queue,
mOldJournal,
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 5883c1c..9eb99ae 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -202,7 +202,7 @@
verify(mObserver)
.restoreSetsAvailable(aryEq(new RestoreSet[] {mRestoreSet1, mRestoreSet2}));
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
assertThat(mWakeLock.isHeld()).isFalse();
}
@@ -235,7 +235,7 @@
verify(mObserver).restoreSetsAvailable(isNull());
assertEventLogged(EventLogTags.RESTORE_TRANSPORT_FAILURE);
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
assertThat(mWakeLock.isHeld()).isFalse();
}
@@ -253,7 +253,7 @@
mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(0);
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
assertThat(mWakeLock.isHeld()).isFalse();
assertThat(mBackupManagerService.isRestoreInProgress()).isFalse();
// Verify it created the task properly
@@ -341,7 +341,7 @@
mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(0);
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
assertThat(mWakeLock.isHeld()).isFalse();
assertThat(mBackupManagerService.isRestoreInProgress()).isFalse();
ShadowPerformUnifiedRestoreTask shadowTask =
@@ -463,7 +463,7 @@
mShadowBackupLooper.runToEndOfTasks();
assertThat(result).isEqualTo(0);
verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ .disposeOfTransportClient(eq(transportMock.mTransportConnection), any());
assertThat(mWakeLock.isHeld()).isFalse();
assertThat(mBackupManagerService.isRestoreInProgress()).isFalse();
ShadowPerformUnifiedRestoreTask shadowTask =
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
index 7dd5be5..ce44f06 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
@@ -36,7 +36,7 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.TransportManager;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
@@ -91,9 +91,9 @@
|| status == TransportStatus.REGISTERED_UNAVAILABLE) {
// Transport registered
when(transportManager.getCurrentTransportClient(any()))
- .thenReturn(transportMock.transportClient);
+ .thenReturn(transportMock.mTransportConnection);
when(transportManager.getCurrentTransportClientOrThrow(any()))
- .thenReturn(transportMock.transportClient);
+ .thenReturn(transportMock.mTransportConnection);
} else {
// Transport not registered
when(transportManager.getCurrentTransportClient(any())).thenReturn(null);
@@ -123,9 +123,9 @@
|| status == TransportStatus.REGISTERED_UNAVAILABLE) {
// Transport registered
when(transportManager.getTransportClient(eq(transportName), any()))
- .thenReturn(transportMock.transportClient);
+ .thenReturn(transportMock.mTransportConnection);
when(transportManager.getTransportClientOrThrow(eq(transportName), any()))
- .thenReturn(transportMock.transportClient);
+ .thenReturn(transportMock.mTransportConnection);
when(transportManager.getTransportName(transportComponent)).thenReturn(transportName);
when(transportManager.getTransportDirName(eq(transportName)))
.thenReturn(transportDirName);
@@ -150,28 +150,28 @@
}
public static TransportMock mockTransport(TransportData transport) throws Exception {
- final TransportClient transportClientMock;
+ final TransportConnection transportConnectionMock;
int status = transport.transportStatus;
ComponentName transportComponent = transport.getTransportComponent();
if (status == TransportStatus.REGISTERED_AVAILABLE
|| status == TransportStatus.REGISTERED_UNAVAILABLE) {
// Transport registered
- transportClientMock = mock(TransportClient.class);
- when(transportClientMock.getTransportComponent()).thenReturn(transportComponent);
+ transportConnectionMock = mock(TransportConnection.class);
+ when(transportConnectionMock.getTransportComponent()).thenReturn(transportComponent);
if (status == TransportStatus.REGISTERED_AVAILABLE) {
// Transport registered and available
IBackupTransport transportMock = mockTransportBinder(transport);
- when(transportClientMock.connectOrThrow(any())).thenReturn(transportMock);
- when(transportClientMock.connect(any())).thenReturn(transportMock);
+ when(transportConnectionMock.connectOrThrow(any())).thenReturn(transportMock);
+ when(transportConnectionMock.connect(any())).thenReturn(transportMock);
- return new TransportMock(transport, transportClientMock, transportMock);
+ return new TransportMock(transport, transportConnectionMock, transportMock);
} else {
// Transport registered but unavailable
- when(transportClientMock.connectOrThrow(any()))
+ when(transportConnectionMock.connectOrThrow(any()))
.thenThrow(TransportNotAvailableException.class);
- when(transportClientMock.connect(any())).thenReturn(null);
+ when(transportConnectionMock.connect(any())).thenReturn(null);
- return new TransportMock(transport, transportClientMock, null);
+ return new TransportMock(transport, transportConnectionMock, null);
}
} else {
// Transport not registered
@@ -198,15 +198,15 @@
public static class TransportMock {
public final TransportData transportData;
- @Nullable public final TransportClient transportClient;
+ @Nullable public final TransportConnection mTransportConnection;
@Nullable public final IBackupTransport transport;
private TransportMock(
TransportData transportData,
- @Nullable TransportClient transportClient,
+ @Nullable TransportConnection transportConnection,
@Nullable IBackupTransport transport) {
this.transportData = transportData;
- this.transportClient = transportClient;
+ this.mTransportConnection = transportConnection;
this.transport = transport;
}
}
diff --git a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionManagerTest.java
similarity index 80%
rename from services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionManagerTest.java
index f033af8..102f594 100644
--- a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionManagerTest.java
@@ -44,14 +44,14 @@
@RunWith(RobolectricTestRunner.class)
@Presubmit
-public class TransportClientManagerTest {
+public class TransportConnectionManagerTest {
private static final String PACKAGE_NAME = "random.package.name";
private static final String CLASS_NAME = "random.package.name.transport.Transport";
@Mock private Context mContext;
@Mock private TransportConnectionListener mTransportConnectionListener;
private @UserIdInt int mUserId;
- private TransportClientManager mTransportClientManager;
+ private TransportConnectionManager mTransportConnectionManager;
private ComponentName mTransportComponent;
private Intent mBindIntent;
@@ -60,8 +60,8 @@
MockitoAnnotations.initMocks(this);
mUserId = UserHandle.USER_SYSTEM;
- mTransportClientManager =
- new TransportClientManager(mUserId, mContext, new TransportStats());
+ mTransportConnectionManager =
+ new TransportConnectionManager(mUserId, mContext, new TransportStats());
mTransportComponent = new ComponentName(PACKAGE_NAME, CLASS_NAME);
mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
@@ -75,11 +75,11 @@
@Test
public void testGetTransportClient() {
- TransportClient transportClient =
- mTransportClientManager.getTransportClient(mTransportComponent, "caller");
+ TransportConnection transportConnection =
+ mTransportConnectionManager.getTransportClient(mTransportComponent, "caller");
// Connect to be able to extract the intent
- transportClient.connectAsync(mTransportConnectionListener, "caller");
+ transportConnection.connectAsync(mTransportConnectionListener, "caller");
verify(mContext)
.bindServiceAsUser(
argThat(matchesIntentAndExtras(mBindIntent)),
@@ -93,10 +93,11 @@
Bundle extras = new Bundle();
extras.putBoolean("random_extra", true);
- TransportClient transportClient =
- mTransportClientManager.getTransportClient(mTransportComponent, extras, "caller");
+ TransportConnection transportConnection =
+ mTransportConnectionManager.getTransportClient(mTransportComponent, extras,
+ "caller");
- transportClient.connectAsync(mTransportConnectionListener, "caller");
+ transportConnection.connectAsync(mTransportConnectionListener, "caller");
mBindIntent.putExtras(extras);
verify(mContext)
.bindServiceAsUser(
@@ -108,13 +109,13 @@
@Test
public void testDisposeOfTransportClient() {
- TransportClient transportClient =
- spy(mTransportClientManager.getTransportClient(mTransportComponent, "caller"));
+ TransportConnection transportConnection =
+ spy(mTransportConnectionManager.getTransportClient(mTransportComponent, "caller"));
- mTransportClientManager.disposeOfTransportClient(transportClient, "caller");
+ mTransportConnectionManager.disposeOfTransportClient(transportConnection, "caller");
- verify(transportClient).unbind(any());
- verify(transportClient).markAsDisposed();
+ verify(transportConnection).unbind(any());
+ verify(transportConnection).markAsDisposed();
}
private ArgumentMatcher<Intent> matchesIntentAndExtras(Intent expectedIntent) {
diff --git a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
similarity index 79%
rename from services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
index 392f2ca..de4aec6 100644
--- a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
@@ -77,7 +77,7 @@
FrameworkShadowLooper.class
})
@Presubmit
-public class TransportClientTest {
+public class TransportConnectionTest {
private static final String PACKAGE_NAME = "some.package.name";
@Mock private Context mContext;
@@ -86,7 +86,7 @@
@Mock private IBackupTransport.Stub mTransportBinder;
@UserIdInt private int mUserId;
private TransportStats mTransportStats;
- private TransportClient mTransportClient;
+ private TransportConnection mTransportConnection;
private ComponentName mTransportComponent;
private String mTransportString;
private Intent mBindIntent;
@@ -106,8 +106,8 @@
mTransportString = mTransportComponent.flattenToShortString();
mTransportStats = new TransportStats();
mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
- mTransportClient =
- new TransportClient(
+ mTransportConnection =
+ new TransportConnection(
mUserId,
mContext,
mTransportStats,
@@ -132,12 +132,12 @@
@Test
public void testGetTransportComponent_returnsTransportComponent() {
- assertThat(mTransportClient.getTransportComponent()).isEqualTo(mTransportComponent);
+ assertThat(mTransportConnection.getTransportComponent()).isEqualTo(mTransportComponent);
}
@Test
public void testConnectAsync_callsBindService() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller");
verify(mContext)
.bindServiceAsUser(
@@ -149,42 +149,42 @@
@Test
public void testConnectAsync_callsListenerWhenConnected() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
- .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
+ .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportConnection));
}
@Test
public void testConnectAsync_whenPendingConnection_callsAllListenersWhenConnected() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
- mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
+ mTransportConnection.connectAsync(mTransportConnectionListener2, "caller2");
connection.onServiceConnected(mTransportComponent, mTransportBinder);
mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
- .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
+ .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportConnection));
verify(mTransportConnectionListener2)
- .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
+ .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportConnection));
}
@Test
public void testConnectAsync_whenAlreadyConnected_callsListener() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
- mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
+ mTransportConnection.connectAsync(mTransportConnectionListener2, "caller2");
mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener2)
- .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
+ .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportConnection));
}
@Test
@@ -196,11 +196,11 @@
any(UserHandle.class)))
.thenReturn(false);
- mTransportClient.connectAsync(mTransportConnectionListener, "caller");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller");
mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
- .onTransportConnectionResult(isNull(), eq(mTransportClient));
+ .onTransportConnectionResult(isNull(), eq(mTransportConnection));
}
@Test
@@ -212,7 +212,7 @@
any(UserHandle.class)))
.thenReturn(false);
- mTransportClient.connectAsync(mTransportConnectionListener, "caller");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
verify(mContext).unbindService(eq(connection));
@@ -220,64 +220,64 @@
@Test
public void testConnectAsync_afterOnServiceDisconnectedBeforeNewConnection_callsListener() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
connection.onServiceDisconnected(mTransportComponent);
- mTransportClient.connectAsync(mTransportConnectionListener2, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener2, "caller1");
verify(mTransportConnectionListener2)
- .onTransportConnectionResult(isNull(), eq(mTransportClient));
+ .onTransportConnectionResult(isNull(), eq(mTransportConnection));
}
@Test
public void testConnectAsync_afterOnServiceDisconnectedAfterNewConnection_callsListener() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
connection.onServiceDisconnected(mTransportComponent);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
- mTransportClient.connectAsync(mTransportConnectionListener2, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener2, "caller1");
// Yes, it should return null because the object became unusable, check design doc
verify(mTransportConnectionListener2)
- .onTransportConnectionResult(isNull(), eq(mTransportClient));
+ .onTransportConnectionResult(isNull(), eq(mTransportConnection));
}
@Test
public void testConnectAsync_callsListenerIfBindingDies() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onBindingDied(mTransportComponent);
mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
- .onTransportConnectionResult(isNull(), eq(mTransportClient));
+ .onTransportConnectionResult(isNull(), eq(mTransportConnection));
}
@Test
public void testConnectAsync_whenPendingConnection_callsListenersIfBindingDies() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
- mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
+ mTransportConnection.connectAsync(mTransportConnectionListener2, "caller2");
connection.onBindingDied(mTransportComponent);
mShadowMainLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
- .onTransportConnectionResult(isNull(), eq(mTransportClient));
+ .onTransportConnectionResult(isNull(), eq(mTransportConnection));
verify(mTransportConnectionListener2)
- .onTransportConnectionResult(isNull(), eq(mTransportClient));
+ .onTransportConnectionResult(isNull(), eq(mTransportConnection));
}
@Test
public void testConnectAsync_beforeFrameworkCall_logsBoundTransitionEvent() {
ShadowEventLog.setUp();
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
}
@@ -285,7 +285,7 @@
@Test
public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitionEvents() {
ShadowEventLog.setUp();
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
@@ -297,7 +297,7 @@
@Test
public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitionEvents() {
ShadowEventLog.setUp();
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onBindingDied(mTransportComponent);
@@ -308,37 +308,37 @@
@Test
public void testConnect_whenConnected_returnsTransport() throws Exception {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
IBackupTransport transportBinder =
- runInWorkerThread(() -> mTransportClient.connect("caller2"));
+ runInWorkerThread(() -> mTransportConnection.connect("caller2"));
assertThat(transportBinder).isNotNull();
}
@Test
public void testConnect_afterOnServiceDisconnected_returnsNull() throws Exception {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
connection.onServiceDisconnected(mTransportComponent);
IBackupTransport transportBinder =
- runInWorkerThread(() -> mTransportClient.connect("caller2"));
+ runInWorkerThread(() -> mTransportConnection.connect("caller2"));
assertThat(transportBinder).isNull();
}
@Test
public void testConnect_afterOnBindingDied_returnsNull() throws Exception {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onBindingDied(mTransportComponent);
IBackupTransport transportBinder =
- runInWorkerThread(() -> mTransportClient.connect("caller2"));
+ runInWorkerThread(() -> mTransportConnection.connect("caller2"));
assertThat(transportBinder).isNull();
}
@@ -350,30 +350,31 @@
// reentrant lock can't be acquired by the listener at the call-site of bindServiceAsUser(),
// which is what would happened if we mocked bindServiceAsUser() to call the listener
// inline.
- TransportClient transportClient = spy(mTransportClient);
+ TransportConnection transportConnection = spy(mTransportConnection);
doAnswer(
invocation -> {
TransportConnectionListener listener = invocation.getArgument(0);
- listener.onTransportConnectionResult(mTransportBinder, transportClient);
+ listener.onTransportConnectionResult(mTransportBinder,
+ transportConnection);
return null;
})
- .when(transportClient)
+ .when(transportConnection)
.connectAsync(any(), any());
IBackupTransport transportBinder =
- runInWorkerThread(() -> transportClient.connect("caller"));
+ runInWorkerThread(() -> transportConnection.connect("caller"));
assertThat(transportBinder).isNotNull();
}
@Test
public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitionEvents() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
ShadowEventLog.setUp();
- mTransportClient.unbind("caller1");
+ mTransportConnection.unbind("caller1");
assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
@@ -382,7 +383,7 @@
@Test
public void
testOnServiceDisconnected_whenConnected_logsDisconnectedAndUnboundTransitionEvents() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
ShadowEventLog.setUp();
@@ -395,7 +396,7 @@
@Test
public void testOnBindingDied_whenConnected_logsDisconnectedAndUnboundTransitionEvents() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
ShadowEventLog.setUp();
@@ -408,60 +409,60 @@
@Test
public void testMarkAsDisposed_whenCreated() {
- mTransportClient.markAsDisposed();
+ mTransportConnection.markAsDisposed();
// No exception thrown
}
@Test
public void testMarkAsDisposed_afterOnBindingDied() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onBindingDied(mTransportComponent);
- mTransportClient.markAsDisposed();
+ mTransportConnection.markAsDisposed();
// No exception thrown
}
@Test
public void testMarkAsDisposed_whenConnectedAndUnbound() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
- mTransportClient.unbind("caller1");
+ mTransportConnection.unbind("caller1");
- mTransportClient.markAsDisposed();
+ mTransportConnection.markAsDisposed();
// No exception thrown
}
@Test
public void testMarkAsDisposed_afterOnServiceDisconnected() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
connection.onServiceDisconnected(mTransportComponent);
- mTransportClient.markAsDisposed();
+ mTransportConnection.markAsDisposed();
// No exception thrown
}
@Test
public void testMarkAsDisposed_whenBound() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
- expectThrows(RuntimeException.class, mTransportClient::markAsDisposed);
+ expectThrows(RuntimeException.class, mTransportConnection::markAsDisposed);
}
@Test
public void testMarkAsDisposed_whenConnected() {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
- expectThrows(RuntimeException.class, mTransportClient::markAsDisposed);
+ expectThrows(RuntimeException.class, mTransportConnection::markAsDisposed);
}
@Test
@@ -469,36 +470,36 @@
public void testFinalize_afterCreated() throws Throwable {
ShadowLog.reset();
- mTransportClient.finalize();
+ mTransportConnection.finalize();
- assertLogcatAtMost(TransportClient.TAG, Log.INFO);
+ assertLogcatAtMost(TransportConnection.TAG, Log.INFO);
}
@Test
@SuppressWarnings("FinalizeCalledExplicitly")
public void testFinalize_whenBound() throws Throwable {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ShadowLog.reset();
- mTransportClient.finalize();
+ mTransportConnection.finalize();
- assertLogcatAtLeast(TransportClient.TAG, Log.ERROR);
+ assertLogcatAtLeast(TransportConnection.TAG, Log.ERROR);
}
@Test
@SuppressWarnings("FinalizeCalledExplicitly")
public void testFinalize_whenConnected() throws Throwable {
- mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ mTransportConnection.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onServiceConnected(mTransportComponent, mTransportBinder);
ShadowLog.reset();
- mTransportClient.finalize();
+ mTransportConnection.finalize();
expectThrows(
TransportNotAvailableException.class,
- () -> mTransportClient.getConnectedTransport("caller1"));
- assertLogcatAtLeast(TransportClient.TAG, Log.ERROR);
+ () -> mTransportConnection.getConnectedTransport("caller1"));
+ assertLogcatAtLeast(TransportConnection.TAG, Log.ERROR);
}
@Test
@@ -506,7 +507,7 @@
public void testFinalize_whenNotMarkedAsDisposed() throws Throwable {
ShadowCloseGuard.setUp();
- mTransportClient.finalize();
+ mTransportConnection.finalize();
assertThat(ShadowCloseGuard.hasReported()).isTrue();
}
@@ -514,10 +515,10 @@
@Test
@SuppressWarnings("FinalizeCalledExplicitly")
public void testFinalize_whenMarkedAsDisposed() throws Throwable {
- mTransportClient.markAsDisposed();
+ mTransportConnection.markAsDisposed();
ShadowCloseGuard.setUp();
- mTransportClient.finalize();
+ mTransportConnection.finalize();
assertThat(ShadowCloseGuard.hasReported()).isFalse();
}
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index adf892a..5dc048b 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@@ -150,7 +151,7 @@
PackageInfo packageInfo, @UserIdInt int userId, int uid) {
when(mPackageManagerInternal.getPackageInfo(
eq(CROSS_PROFILE_APP_PACKAGE_NAME),
- /* flags= */ anyInt(),
+ /* flags= */ anyLong(),
/* filterCallingUid= */ anyInt(),
eq(userId)))
.thenReturn(packageInfo);
@@ -469,7 +470,7 @@
private void mockUninstallCrossProfileAppFromWorkProfile() {
when(mPackageManagerInternal.getPackageInfo(
eq(CROSS_PROFILE_APP_PACKAGE_NAME),
- /* flags= */ anyInt(),
+ /* flags= */ anyLong(),
/* filterCallingUid= */ anyInt(),
eq(WORK_PROFILE_USER_ID)))
.thenReturn(null);
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupEligibilityRules.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupEligibilityRules.java
index 566b0e1..9a74977 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupEligibilityRules.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupEligibilityRules.java
@@ -19,9 +19,8 @@
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
import org.robolectric.annotation.Implementation;
@@ -54,7 +53,7 @@
@Implementation
protected boolean appIsRunningAndEligibleForBackupWithTransport(
- @Nullable TransportClient transportClient,
+ @Nullable TransportConnection transportConnection,
String packageName) {
return sAppsRunningAndEligibleForBackupWithTransport.contains(packageName);
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
index fd51df7..06b7fb7 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -23,7 +23,7 @@
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.keyvalue.KeyValueBackupReporter;
import com.android.server.backup.keyvalue.KeyValueBackupTask;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
import org.robolectric.annotation.Implementation;
@@ -56,7 +56,7 @@
@Implementation
protected void __constructor__(
UserBackupManagerService backupManagerService,
- TransportClient transportClient,
+ TransportConnection transportConnection,
String transportDirName,
List<String> queue,
@Nullable DataChangedJournal dataChangedJournal,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 5161070..71010a9 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -24,15 +24,12 @@
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import java.util.Map;
-import java.util.Set;
-
@Implements(PerformUnifiedRestoreTask.class)
public class ShadowPerformUnifiedRestoreTask {
@Nullable private static ShadowPerformUnifiedRestoreTask sLastShadow;
@@ -60,7 +57,7 @@
@Implementation
protected void __constructor__(
UserBackupManagerService backupManagerService,
- TransportClient transportClient,
+ TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
long restoreSetToken,
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 9d86081..4b12fd4 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -274,7 +274,7 @@
private fun makePkgSetting(pkgName: String) = spy(
PackageSetting(
pkgName, null, File("/test"),
- null, null, null, null, 0, 0, 0, 0, null, null, null,
+ null, null, null, null, 0, 0, 0, 0, null, null, null, null, null,
UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c")
)
) {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index dc93e53..4a35fdf 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -106,6 +106,16 @@
"setSplitCodePaths",
"setSplitClassLoaderName",
"setSplitHasCode",
+ // Tested through addUsesSdkLibrary
+ "addUsesSdkLibrary",
+ "getUsesSdkLibraries",
+ "getUsesSdkLibrariesVersionsMajor",
+ "getUsesSdkLibrariesCertDigests",
+ // Tested through addUsesStaticLibrary
+ "addUsesStaticLibrary",
+ "getUsesStaticLibraries",
+ "getUsesStaticLibrariesVersions",
+ "getUsesStaticLibrariesCertDigests"
)
override val baseParams = listOf(
@@ -157,6 +167,8 @@
AndroidPackage::getSecondaryNativeLibraryDir,
AndroidPackage::getSharedUserId,
AndroidPackage::getSharedUserLabel,
+ AndroidPackage::getSdkLibName,
+ AndroidPackage::getSdkLibVersionMajor,
AndroidPackage::getStaticSharedLibName,
AndroidPackage::getStaticSharedLibVersion,
AndroidPackage::getTargetSandboxVersion,
@@ -210,6 +222,7 @@
AndroidPackage::isResizeableActivityViaSdkVersion,
AndroidPackage::isRestoreAnyVersion,
AndroidPackage::isSignedWithPlatformKey,
+ AndroidPackage::isSdkLibrary,
AndroidPackage::isStaticSharedLibrary,
AndroidPackage::isStub,
AndroidPackage::isSupportsRtl,
@@ -228,7 +241,7 @@
AndroidPackage::getMinAspectRatio,
AndroidPackage::hasPreserveLegacyExternalStorage,
AndroidPackage::hasRequestForegroundServiceExemption,
- AndroidPackage::hasRequestRawExternalStorageAccess,
+ AndroidPackage::hasRequestRawExternalStorageAccess
)
override fun extraParams() = listOf(
@@ -254,13 +267,6 @@
adder(AndroidPackage::getUsesNativeLibraries, "testUsesNativeLibrary"),
adder(AndroidPackage::getUsesOptionalLibraries, "testUsesOptionalLibrary"),
adder(AndroidPackage::getUsesOptionalNativeLibraries, "testUsesOptionalNativeLibrary"),
- adder(AndroidPackage::getUsesStaticLibraries, "testUsesStaticLibrary"),
- getSetByValue(
- AndroidPackage::getUsesStaticLibrariesVersions,
- PackageImpl::addUsesStaticLibraryVersion,
- (testCounter++).toLong(),
- transformGet = { it?.singleOrNull() }
- ),
getSetByValue(
AndroidPackage::areAttributionsUserVisible,
ParsingPackage::setAttributionsAreUserVisible,
@@ -290,7 +296,7 @@
AndroidPackage::getKeySetMapping,
PackageImpl::addKeySet,
"testKeySetName" to testKey(),
- transformGet = { "testKeySetName" to it["testKeySetName"]?.singleOrNull() },
+ transformGet = { "testKeySetName" to it["testKeySetName"]?.singleOrNull() }
),
getSetByValue(
AndroidPackage::getPermissionGroups,
@@ -315,7 +321,7 @@
{ it.first },
{ it.second.intentFilter.schemesIterator().asSequence().singleOrNull() },
{ it.second.intentFilter.authoritiesIterator().asSequence()
- .singleOrNull()?.host },
+ .singleOrNull()?.host }
)
}
),
@@ -324,7 +330,7 @@
PackageImpl::addQueriesIntent,
Intent(Intent.ACTION_VIEW, Uri.parse("https://test.pm.server.android.com")),
transformGet = { it.singleOrNull() },
- compare = { first, second -> first?.filterEquals(second) },
+ compare = { first, second -> first?.filterEquals(second) }
),
getSetByValue(
AndroidPackage::getRestrictUpdateHash,
@@ -347,13 +353,6 @@
}
),
getSetByValue(
- AndroidPackage::getUsesStaticLibrariesCertDigests,
- PackageImpl::addUsesStaticLibraryCertDigests,
- arrayOf("testCertDigest"),
- transformGet = { it?.singleOrNull() },
- compare = Array<String?>?::contentEquals
- ),
- getSetByValue(
AndroidPackage::getActivities,
PackageImpl::addActivity,
"TestActivityName",
@@ -440,7 +439,7 @@
first, second,
{ it.size() },
{ it.keyAt(0) },
- { it.valueAt(0) },
+ { it.valueAt(0) }
)
}
),
@@ -451,7 +450,7 @@
compare = { first, second ->
equalBy(
first, second,
- { it["testProcess"]?.name },
+ { it["testProcess"]?.name }
)
}
),
@@ -471,10 +470,10 @@
PackageManager.Property::getName,
PackageManager.Property::getClassName,
PackageManager.Property::getPackageName,
- PackageManager.Property::getString,
+ PackageManager.Property::getString
)
}
- ),
+ )
)
override fun initialObject() = PackageImpl.forParsing(
@@ -518,6 +517,9 @@
.setSplitClassLoaderName(0, "testSplitClassLoaderNameZero")
.setSplitClassLoaderName(1, "testSplitClassLoaderNameOne")
+ .addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"))
+ .addUsesStaticLibrary("testStatic", 3L, arrayOf("testCertDigest2"))
+
override fun extraAssertions(before: Parcelable, after: Parcelable) {
super.extraAssertions(before, after)
after as PackageImpl
@@ -558,6 +560,18 @@
expect.that(it.get(0)).asList().containsExactly(-1)
expect.that(it.get(1)).asList().containsExactly(0)
}
+
+ expect.that(after.usesSdkLibraries).containsExactly("testSdk")
+ expect.that(after.usesSdkLibrariesVersionsMajor).asList().containsExactly(2L)
+ expect.that(after.usesSdkLibrariesCertDigests!!.size).isEqualTo(1)
+ expect.that(after.usesSdkLibrariesCertDigests!![0]).asList()
+ .containsExactly("testCertDigest1")
+
+ expect.that(after.usesStaticLibraries).containsExactly("testStatic")
+ expect.that(after.usesStaticLibrariesVersions).asList().containsExactly(3L)
+ expect.that(after.usesStaticLibrariesCertDigests!!.size).isEqualTo(1)
+ expect.that(after.usesStaticLibrariesCertDigests!![0]).asList()
+ .containsExactly("testCertDigest2")
}
private fun testKey() = KeyPairGenerator.getInstance("RSA")
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationValidIntentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationValidIntentTest.kt
index 98634b2..ac6b679 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationValidIntentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationValidIntentTest.kt
@@ -120,7 +120,7 @@
@Test
fun verify() {
val flags = if (params.matchDefaultOnly) PackageManager.MATCH_DEFAULT_ONLY else 0
- assertThat(DomainVerificationUtils.isDomainVerificationIntent(params.intent, flags))
- .isEqualTo(params.expected)
+ assertThat(DomainVerificationUtils.isDomainVerificationIntent(params.intent,
+ flags.toLong())).isEqualTo(params.expected)
}
}
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 a9099ae..5885470 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -2961,7 +2961,7 @@
private void registerAppIds(String[] packages, Integer[] ids) {
assertEquals(packages.length, ids.length);
- when(mPackageManagerInternal.getPackageUid(anyString(), anyInt(), anyInt())).thenAnswer(
+ when(mPackageManagerInternal.getPackageUid(anyString(), anyLong(), anyInt())).thenAnswer(
invocation -> {
final String pkg = invocation.getArgument(0);
final int index = ArrayUtils.indexOf(packages, pkg);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index b2847ce..783971a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -80,7 +81,7 @@
doReturn(mActivityManagerInternal).when(
() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mIPackageManager).when(() -> AppGlobals.getPackageManager());
- when(mIPackageManager.getPackageUid(eq(TEST_PACKAGE_NAME), anyInt(), anyInt())).thenReturn(
+ when(mIPackageManager.getPackageUid(eq(TEST_PACKAGE_NAME), anyLong(), anyInt())).thenReturn(
TEST_CALLING_UID);
ActivityManagerConstants constants = mock(ActivityManagerConstants.class);
constants.PENDINGINTENT_WARNING_THRESHOLD = 2000;
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java
new file mode 100644
index 0000000..3f69f1b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.server.display;
+
+import static com.android.server.display.DensityMap.Entry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DensityMapTest {
+
+ @Test
+ public void testConstructor_withBadConfig_throwsException() {
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(1080, 1920, 320)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(1920, 1080, 120)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 120)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(3840, 2160, 120)})
+ );
+
+ // Two entries with the same diagonal
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(500, 500, 123),
+ new Entry(100, 700, 456)})
+ );
+ }
+
+ @Test
+ public void testGetDensityForResolution_withResolutionMatch_returnsDensityFromConfig() {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(720, 1280, 213),
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 640)});
+
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1920, 1080));
+
+ assertEquals(640, densityMap.getDensityForResolution(2160, 3840));
+ assertEquals(640, densityMap.getDensityForResolution(3840, 2160));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withDiagonalMatch_returnsDensityFromConfig() {
+ DensityMap densityMap = DensityMap.createByOwning(
+ new Entry[]{ new Entry(500, 500, 123)});
+
+ // 500x500 has the same diagonal as 100x700
+ assertEquals(123, densityMap.getDensityForResolution(100, 700));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withOneEntry_withNoMatch_returnsExtrapolatedDensity() {
+ DensityMap densityMap = DensityMap.createByOwning(
+ new Entry[]{ new Entry(1080, 1920, 320)});
+
+ assertEquals(320, densityMap.getDensityForResolution(1081, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1921));
+
+ assertEquals(640, densityMap.getDensityForResolution(2160, 3840));
+ assertEquals(640, densityMap.getDensityForResolution(3840, 2160));
+
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withTwoEntries_withNoMatch_returnExtrapolatedDensity() {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 320)});
+
+ // Resolution is smaller than all entries
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+
+ // Resolution is bigger than all entries
+ assertEquals(320 * 2, densityMap.getDensityForResolution(2160 * 2, 3840 * 2));
+ assertEquals(320 * 2, densityMap.getDensityForResolution(3840 * 2, 2160 * 2));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withNoMatch_returnsInterpolatedDensity() {
+ {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 320)});
+
+ assertEquals(320, densityMap.getDensityForResolution(2000, 2000));
+ }
+
+ {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(720, 1280, 213),
+ new Entry(2160, 3840, 640)});
+
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1920, 1080));
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index b17ff53b..95912b2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -43,6 +43,7 @@
import android.app.job.JobInfo;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener;
+import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
@@ -168,12 +169,15 @@
}
}
- private JobStatus createJobStatus(String testTag, int jobId) {
- JobInfo jobInfo = new JobInfo.Builder(jobId,
+ private JobInfo createJobInfo(int jobId) {
+ return new JobInfo.Builder(jobId,
new ComponentName(mContext, "TestPrefetchJobService"))
.setPrefetch(true)
.build();
- return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo);
+ }
+
+ private JobStatus createJobStatus(String testTag, int jobId) {
+ return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, createJobInfo(jobId));
}
private static JobStatus createJobStatus(String testTag, String packageName, int callingUid,
@@ -331,6 +335,32 @@
}
@Test
+ public void testConstraintSatisfiedWhenWidget() {
+ final JobStatus jobNonWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 1);
+ final JobStatus jobWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 2);
+
+ when(mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(sSystemClock.millis() + 100 * HOUR_IN_MILLIS);
+
+ final AppWidgetManager appWidgetManager = mock(AppWidgetManager.class);
+ when(mContext.getSystemService(AppWidgetManager.class)).thenReturn(appWidgetManager);
+ mPrefetchController.onSystemServicesReady();
+
+ when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(false);
+ trackJobs(jobNonWidget);
+ verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ assertFalse(jobNonWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+
+ when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(true);
+ trackJobs(jobWidget);
+ assertTrue(jobWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ }
+
+ @Test
public void testEstimatedLaunchTimeChangedToLate() {
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
when(mUsageStatsManagerInternal
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
index e2e7f5d..94dcdf9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
@@ -58,72 +58,86 @@
@Test
public void testLocationMonitoring() {
CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
- Object key1 = new Object();
- Object key2 = new Object();
CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
- Object key3 = new Object();
- Object key4 = new Object();
- mHelper.reportLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
+ mHelper.reportLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
+ mHelper.reportLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
+ mHelper.reportLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
- mHelper.reportLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
+ mHelper.reportLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
- mHelper.reportLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
- mHelper.reportLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
+ mHelper.reportLocationStop(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ mHelper.reportLocationStop(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller1));
- mHelper.reportLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
- mHelper.reportLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
+ mHelper.reportLocationStop(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ mHelper.reportLocationStop(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller2));
}
@Test
public void testHighPowerLocationMonitoring() {
CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
- Object key1 = new Object();
- Object key2 = new Object();
CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
- Object key3 = new Object();
- Object key4 = new Object();
- mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ mHelper.reportHighPowerLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ mHelper.reportHighPowerLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ mHelper.reportHighPowerLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
- mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ mHelper.reportHighPowerLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
- mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ mHelper.reportHighPowerLocationStop(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ mHelper.reportHighPowerLocationStop(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
- mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ mHelper.reportHighPowerLocationStop(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ mHelper.reportHighPowerLocationStop(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index d0b2eda..890a549 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -845,6 +845,48 @@
}
@Test
+ public void testLocationMonitoring_multipleIdentities() {
+ CallerIdentity identity1 = CallerIdentity.forTest(CURRENT_USER, 1,
+ "mypackage", "attribution", "listener1");
+ CallerIdentity identity2 = CallerIdentity.forTest(CURRENT_USER, 1,
+ "mypackage", "attribution", "listener2");
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ IDENTITY.getPackageName())).isFalse();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ IDENTITY.getPackageName())).isFalse();
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build();
+ mManager.registerLocationRequest(request1, identity1, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build();
+ mManager.registerLocationRequest(request2, identity2, PERMISSION_FINE, listener2);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isTrue();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener2);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isTrue();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener1);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isFalse();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isFalse();
+ }
+
+ @Test
public void testProviderRequest() {
assertThat(mProvider.getRequest().isActive()).isFalse();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 63416c9..ae5984a41 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -47,6 +47,7 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito.any
import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean
import com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt
+import com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong
import com.android.dx.mockito.inline.extended.ExtendedMockito.anyString
import com.android.dx.mockito.inline.extended.ExtendedMockito.argThat
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -149,7 +150,7 @@
}
whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(),
nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(),
- nullable(), nullable(), nullable(), nullable())) {
+ nullable(), nullable(), nullable(), nullable(), nullable(), nullable())) {
val name: String = getArgument(0)
val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name }
?: return@whenever null
@@ -617,7 +618,7 @@
private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) {
whenever(mocks.componentResolver.queryActivities(
argThat { intent: Intent? -> intent != null && (action == intent.action) },
- nullable(), anyInt(), anyInt())) {
+ nullable(), anyLong(), anyInt())) {
ArrayList(activities.asList().map { info: ActivityInfo? ->
ResolveInfo().apply { activityInfo = info }
})
@@ -627,7 +628,7 @@
private fun mockQueryServices(action: String, vararg services: ServiceInfo) {
whenever(mocks.componentResolver.queryServices(
argThat { intent: Intent? -> intent != null && (action == intent.action) },
- nullable(), anyInt(), anyInt())) {
+ nullable(), anyLong(), anyInt())) {
ArrayList(services.asList().map { info ->
ResolveInfo().apply { serviceInfo = info }
})
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index bbfa461..cfc81e6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -29,6 +29,7 @@
import com.android.server.apphibernation.AppHibernationService
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.testutils.whenever
+import org.junit.Assert
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -36,7 +37,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -102,9 +102,10 @@
val pm = createPackageManagerService()
rule.system().validateFinalState()
- pm.setPackageStoppedState(TEST_PACKAGE_NAME, true, TEST_USER_ID)
TestableLooper.get(this).processAllMessages()
- Mockito.clearInvocations(appHibernationManager)
+
+ whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(true)
pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
@@ -115,6 +116,31 @@
}
@Test
+ fun testExitForceStop_nonExistingAppHibernationManager_doesNotThrowException() {
+ whenever(rule.mocks().injector.getLocalService(AppHibernationManagerInternal::class.java))
+ .thenReturn(null)
+
+ rule.system().stageScanExistingPackage(
+ TEST_PACKAGE_NAME,
+ 1L,
+ rule.system().dataAppDirectory)
+ val pm = createPackageManagerService()
+ rule.system().validateFinalState()
+
+ TestableLooper.get(this).processAllMessages()
+
+ whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(true)
+
+ try {
+ pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
+ TestableLooper.get(this).processAllMessages()
+ } catch (e: Exception) {
+ Assert.fail("Method throws exception when AppHibernationManager is not ready.\n$e")
+ }
+ }
+
+ @Test
fun testGetOptimizablePackages_ExcludesGloballyHibernatingPackages() {
rule.system().stageScanExistingPackage(
TEST_PACKAGE_NAME,
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index fe23c14..9666758 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -41,6 +41,7 @@
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.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -316,14 +317,14 @@
final int lastUserId = 5;
final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent,
PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0);
- doReturn(pi).when(mIpm).getServiceInfo(any(), anyInt(), anyInt());
+ doReturn(pi).when(mIpm).getServiceInfo(any(), anyLong(), anyInt());
final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
final ParceledListSlice ris =
mIpm.queryIntentServices(intent,
intent.resolveTypeIfNeeded(sContext.getContentResolver()),
PackageManager.GET_META_DATA, 0);
- doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyInt(), anyInt());
+ doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyLong(), anyInt());
doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission(
eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt());
@@ -348,7 +349,7 @@
final int lastUserId = 5;
final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent,
PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0);
- doReturn(pi).when(mIpm).getServiceInfo(any(), anyInt(), anyInt());
+ doReturn(pi).when(mIpm).getServiceInfo(any(), anyLong(), anyInt());
final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
final ParceledListSlice ris =
@@ -362,7 +363,7 @@
mService.switchUser(userId, null);
verifyLastWallpaperData(userId, sImageWallpaperComponentName);
// Simulate user unlocked
- doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyInt(), eq(userId));
+ doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyLong(), eq(userId));
mService.onUnlockUser(userId);
verifyLastWallpaperData(userId, sDefaultWallpaperComponent);
verifyCurrentSystemData(userId);
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ba580ec..e3c60fd 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -108,6 +108,7 @@
data: [
":JobTestApp",
+ ":StubTestApp",
],
java_resources: [
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 5a0f1ee..bb3eb81 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -16,6 +16,13 @@
<configuration description="Runs Frameworks Services Tests.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="SimpleServiceTestApp3.apk"
+ value="/data/local/tmp/cts/content/SimpleServiceTestApp3.apk" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
@@ -28,6 +35,17 @@
<option name="test-file-name" value="SimpleServiceTestApp3.apk" />
</target_preparer>
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/servicestests" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/servicestests"/>
+ </target_preparer>
+
+ <!-- Load additional APKs onto device -->
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="StubTestApp.apk->/data/local/tmp/servicestests/StubTestApp.apk"/>
+ </target_preparer>
+
<option name="test-tag" value="FrameworksServicesTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.servicestests" />
diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
index 3ace3f4..a1d4c20 100644
--- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
@@ -66,7 +66,7 @@
when(mHelper.isBluetoothOn()).thenReturn(true);
Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
- when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+ when(mHelper.isMediaProfileConnected()).thenReturn(true);
Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
when(mHelper.isAirplaneModeOn()).thenReturn(true);
@@ -83,7 +83,7 @@
public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() {
mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
when(mHelper.isBluetoothOn()).thenReturn(true);
- when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+ when(mHelper.isMediaProfileConnected()).thenReturn(true);
when(mHelper.isAirplaneModeOn()).thenReturn(true);
mBluetoothAirplaneModeListener.handleAirplaneModeChange();
@@ -97,7 +97,7 @@
public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() {
mBluetoothAirplaneModeListener.mToastCount = 0;
when(mHelper.isBluetoothOn()).thenReturn(true);
- when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+ when(mHelper.isMediaProfileConnected()).thenReturn(true);
when(mHelper.isAirplaneModeOn()).thenReturn(true);
mBluetoothAirplaneModeListener.handleAirplaneModeChange();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 086e3c0..a83d51b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -68,6 +68,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.MagnificationConfig;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -650,8 +651,12 @@
final float scale = 1.8f;
final float centerX = 50.5f;
final float centerY = 100.5f;
- when(mMockMagnificationProcessor.setScaleAndCenter(displayId,
- scale, centerX, centerY, true, SERVICE_ID)).thenReturn(true);
+ MagnificationConfig config = new MagnificationConfig.Builder()
+ .setScale(scale)
+ .setCenterX(centerX)
+ .setCenterY(centerY).build();
+ when(mMockMagnificationProcessor.setMagnificationConfig(displayId, config, true,
+ SERVICE_ID)).thenReturn(true);
when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
final boolean result = mServiceConnection.setMagnificationScaleAndCenter(
@@ -665,8 +670,12 @@
final float scale = 1.8f;
final float centerX = 50.5f;
final float centerY = 100.5f;
- when(mMockMagnificationProcessor.setScaleAndCenter(displayId,
- scale, centerX, centerY, true, SERVICE_ID)).thenReturn(true);
+ MagnificationConfig config = new MagnificationConfig.Builder()
+ .setScale(scale)
+ .setCenterX(centerX)
+ .setCenterY(centerY).build();
+ when(mMockMagnificationProcessor.setMagnificationConfig(displayId, config, true,
+ SERVICE_ID)).thenReturn(true);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final boolean result = mServiceConnection.setMagnificationScaleAndCenter(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index c412b94..205c3da 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -16,25 +16,33 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.MagnificationConfig.FULLSCREEN_MODE;
+import static android.accessibilityservice.MagnificationConfig.WINDOW_MODE;
+
import static org.junit.Assert.assertEquals;
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.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.accessibilityservice.MagnificationConfig;
import android.graphics.Region;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
+import com.android.server.accessibility.magnification.WindowMagnificationManager;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-
+import org.mockito.stubbing.Answer;
/**
* Tests for the {@link MagnificationProcessor}
@@ -42,57 +50,115 @@
public class MagnificationProcessorTest {
private static final int TEST_DISPLAY = 0;
-
+ private static final int SERVICE_ID = 42;
+ private static final float TEST_SCALE = 1.8f;
+ private static final float TEST_CENTER_X = 50.5f;
+ private static final float TEST_CENTER_Y = 100.5f;
private MagnificationProcessor mMagnificationProcessor;
@Mock
private MagnificationController mMockMagnificationController;
@Mock
private FullScreenMagnificationController mMockFullScreenMagnificationController;
+ @Mock
+ private WindowMagnificationManager mMockWindowMagnificationManager;
+ FullScreenMagnificationControllerStub mFullScreenMagnificationControllerStub;
+ WindowMagnificationManagerStub mWindowMagnificationManagerStub;
@Before
+
public void setup() {
MockitoAnnotations.initMocks(this);
-
+ mFullScreenMagnificationControllerStub = new FullScreenMagnificationControllerStub(
+ mMockFullScreenMagnificationController);
+ mWindowMagnificationManagerStub = new WindowMagnificationManagerStub(
+ mMockWindowMagnificationManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
+ when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
+ mMockWindowMagnificationManager);
mMagnificationProcessor = new MagnificationProcessor(mMockMagnificationController);
}
@Test
- public void getScale() {
- final float result = 2;
- when(mMockFullScreenMagnificationController.getScale(TEST_DISPLAY)).thenReturn(result);
+ public void getScale_fullscreenMode_expectedValue() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setScale(TEST_SCALE).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
float scale = mMagnificationProcessor.getScale(TEST_DISPLAY);
- assertEquals(scale, result, 0);
+ assertEquals(scale, TEST_SCALE, 0);
}
@Test
- public void getCenterX_canControlMagnification_returnCenterX() {
- final float result = 200;
- when(mMockFullScreenMagnificationController.getCenterX(TEST_DISPLAY)).thenReturn(result);
+ public void getScale_windowMode_expectedValue() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setScale(TEST_SCALE).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ float scale = mMagnificationProcessor.getScale(TEST_DISPLAY);
+
+ assertEquals(scale, TEST_SCALE, 0);
+ }
+
+ @Test
+ public void getCenterX_canControlFullscreenMagnification_returnCenterX() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setCenterX(TEST_CENTER_X).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
float centerX = mMagnificationProcessor.getCenterX(
TEST_DISPLAY, /* canControlMagnification= */true);
- assertEquals(centerX, result, 0);
+ assertEquals(centerX, TEST_CENTER_X, 0);
}
@Test
- public void getCenterY_canControlMagnification_returnCenterY() {
- final float result = 300;
- when(mMockFullScreenMagnificationController.getCenterY(TEST_DISPLAY)).thenReturn(result);
+ public void getCenterX_canControlWindowMagnification_returnCenterX() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setCenterX(TEST_CENTER_X).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ float centerX = mMagnificationProcessor.getCenterX(
+ TEST_DISPLAY, /* canControlMagnification= */true);
+
+ assertEquals(centerX, TEST_CENTER_X, 0);
+ }
+
+ @Test
+ public void getCenterY_canControlFullscreenMagnification_returnCenterY() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
float centerY = mMagnificationProcessor.getCenterY(
TEST_DISPLAY, /* canControlMagnification= */false);
- assertEquals(centerY, result, 0);
+ assertEquals(centerY, TEST_CENTER_Y, 0);
}
@Test
- public void getMagnificationRegion_canControlMagnification_returnRegion() {
+ public void getCenterY_canControlWindowMagnification_returnCenterY() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ float centerY = mMagnificationProcessor.getCenterY(
+ TEST_DISPLAY, /* canControlMagnification= */false);
+
+ assertEquals(centerY, TEST_CENTER_Y, 0);
+ }
+
+ @Test
+ public void getMagnificationRegion_canControlFullscreenMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
region, /* canControlMagnification= */true);
@@ -101,14 +167,25 @@
}
@Test
- public void getMagnificationRegion_notRegistered_shouldRegisterThenUnregister() {
+ public void getMagnificationRegion_canControlWindowMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, WINDOW_MODE);
+ mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
+ region, /* canControlMagnification= */true);
+
+ verify(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+ eq(region));
+ }
+
+ @Test
+ public void getMagnificationRegion_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
+ final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
}).when(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY),
any());
- when(mMockFullScreenMagnificationController.isRegistered(TEST_DISPLAY)).thenReturn(false);
final Region result = new Region();
mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
@@ -119,44 +196,270 @@
}
@Test
- public void getMagnificationCenterX_notRegistered_shouldRegisterThenUnregister() {
- final float centerX = 480.0f;
- when(mMockFullScreenMagnificationController.getCenterX(TEST_DISPLAY)).thenReturn(centerX);
- when(mMockFullScreenMagnificationController.isRegistered(TEST_DISPLAY)).thenReturn(false);
+ public void getMagnificationCenterX_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setCenterX(TEST_CENTER_X).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
final float result = mMagnificationProcessor.getCenterX(
TEST_DISPLAY, /* canControlMagnification= */ true);
- assertEquals(centerX, result, 0);
+ assertEquals(TEST_CENTER_X, result, 0);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
verify(mMockFullScreenMagnificationController).unregister(TEST_DISPLAY);
}
@Test
- public void getMagnificationCenterY_notRegistered_shouldRegisterThenUnregister() {
- final float centerY = 640.0f;
- when(mMockFullScreenMagnificationController.getCenterY(TEST_DISPLAY)).thenReturn(centerY);
- when(mMockFullScreenMagnificationController.isRegistered(TEST_DISPLAY)).thenReturn(false);
+ public void getMagnificationCenterY_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
final float result = mMagnificationProcessor.getCenterY(
TEST_DISPLAY, /* canControlMagnification= */ true);
- assertEquals(centerY, result, 0);
+ assertEquals(TEST_CENTER_Y, result, 0);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
verify(mMockFullScreenMagnificationController).unregister(TEST_DISPLAY);
}
@Test
- public void setMagnificationScaleAndCenter_notRegistered_shouldRegister() {
- final int serviceId = 42;
- final float scale = 1.8f;
- final float centerX = 50.5f;
- final float centerY = 100.5f;
- when(mMockFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
- scale, centerX, centerY, true, serviceId)).thenReturn(true);
- when(mMockFullScreenMagnificationController.isRegistered(TEST_DISPLAY)).thenReturn(false);
+ public void getCurrentMode_configDefaultMode_returnActivatedMode() {
+ final int targetMode = WINDOW_MODE;
+ setMagnificationActivated(TEST_DISPLAY, targetMode);
- final boolean result = mMagnificationProcessor.setScaleAndCenter(
- TEST_DISPLAY, scale, centerX, centerY, true, serviceId);
+ int currentMode = mMagnificationProcessor.getControllingMode(TEST_DISPLAY);
+
+ assertEquals(WINDOW_MODE, currentMode);
+ }
+
+ @Test
+ public void reset_fullscreenMagnificationActivated() {
+ setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
+
+ mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+
+ verify(mMockFullScreenMagnificationController).reset(TEST_DISPLAY, false);
+ }
+
+ @Test
+ public void reset_windowMagnificationActivated() {
+ setMagnificationActivated(TEST_DISPLAY, WINDOW_MODE);
+
+ mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+
+ verify(mMockWindowMagnificationManager).reset(TEST_DISPLAY);
+ }
+
+ @Test
+ public void setMagnificationConfig_fullscreenModeNotRegistered_shouldRegister() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ final boolean result = mMagnificationProcessor.setMagnificationConfig(
+ TEST_DISPLAY, config, true, SERVICE_ID);
assertTrue(result);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
}
+
+ @Test
+ public void setMagnificationConfig_windowMode_enableMagnification() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ final boolean result = mMagnificationProcessor.setMagnificationConfig(
+ TEST_DISPLAY, config, true, SERVICE_ID);
+
+ assertTrue(result);
+ }
+
+ @Test
+ public void setMagnificationConfig_fullscreenEnabled_expectedConfigValues() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+ // mMockFullScreenMagnificationController.unregister(TEST_DISPLAY);
+ mMagnificationProcessor.setMagnificationConfig(
+ TEST_DISPLAY, config, true, SERVICE_ID);
+
+ final MagnificationConfig result = mMagnificationProcessor.getMagnificationConfig(
+ TEST_DISPLAY);
+
+ assertConfigEquals(config, result);
+ }
+
+ @Test
+ public void setMagnificationConfig_windowEnabled_expectedConfigValues() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ mMagnificationProcessor.setMagnificationConfig(
+ TEST_DISPLAY, config, true, SERVICE_ID);
+
+ final MagnificationConfig result = mMagnificationProcessor.getMagnificationConfig(
+ TEST_DISPLAY);
+
+ assertConfigEquals(config, result);
+ }
+
+ @Test
+ public void setMagnificationConfig_controllingModeChangeAndAnimating_transitionConfigMode() {
+ final int currentActivatedMode = WINDOW_MODE;
+ final int targetMode = FULLSCREEN_MODE;
+ final MagnificationConfig oldConfig = new MagnificationConfig.Builder()
+ .setMode(currentActivatedMode)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, oldConfig);
+ final MagnificationConfig newConfig = new MagnificationConfig.Builder()
+ .setMode(targetMode)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X + 10)
+ .setCenterY(TEST_CENTER_Y + 10).build();
+
+ // Has magnification animation running
+ when(mMockMagnificationController.hasDisableMagnificationCallback(TEST_DISPLAY)).thenReturn(
+ true);
+ setMagnificationActivated(TEST_DISPLAY, newConfig);
+
+ final MagnificationConfig result = mMagnificationProcessor.getMagnificationConfig(
+ TEST_DISPLAY);
+ verify(mMockMagnificationController).transitionMagnificationConfigMode(eq(TEST_DISPLAY),
+ eq(newConfig), anyBoolean());
+ assertConfigEquals(newConfig, result);
+ }
+
+ private void setMagnificationActivated(int displayId, int configMode) {
+ setMagnificationActivated(displayId,
+ new MagnificationConfig.Builder().setMode(configMode).build());
+ }
+
+ private void setMagnificationActivated(int displayId, MagnificationConfig config) {
+ when(mMockMagnificationController.isActivated(displayId, config.getMode())).thenReturn(
+ true);
+ mMagnificationProcessor.setMagnificationConfig(displayId, config, false, SERVICE_ID);
+ if (config.getMode() == FULLSCREEN_MODE) {
+ when(mMockMagnificationController.isActivated(displayId, WINDOW_MODE)).thenReturn(
+ false);
+ mFullScreenMagnificationControllerStub.resetAndStubMethods();
+ mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(),
+ config.getCenterX(), config.getCenterY(), true, SERVICE_ID);
+ } else if (config.getMode() == WINDOW_MODE) {
+ when(mMockMagnificationController.isActivated(displayId, FULLSCREEN_MODE)).thenReturn(
+ false);
+ mWindowMagnificationManagerStub.resetAndStubMethods();
+ mMockWindowMagnificationManager.enableWindowMagnification(displayId, config.getScale(),
+ config.getCenterX(), config.getCenterY());
+ }
+ }
+
+ private void assertConfigEquals(MagnificationConfig expected, MagnificationConfig actual) {
+ assertEquals(expected.getMode(), actual.getMode());
+ assertEquals(expected.getScale(), actual.getScale(), 0);
+ assertEquals(expected.getCenterX(), actual.getCenterX(), 0);
+ assertEquals(expected.getCenterY(), actual.getCenterY(), 0);
+ }
+
+ private static class FullScreenMagnificationControllerStub {
+ private final FullScreenMagnificationController mScreenMagnificationController;
+ private float mScale = 1.0f;
+ private float mCenterX = 0;
+ private float mCenterY = 0;
+ private boolean mIsRegistered = false;
+
+ FullScreenMagnificationControllerStub(
+ FullScreenMagnificationController screenMagnificationController) {
+ mScreenMagnificationController = screenMagnificationController;
+ }
+
+ private void stubMethods() {
+ doAnswer(invocation -> mScale).when(mScreenMagnificationController).getScale(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mCenterX).when(mScreenMagnificationController).getCenterX(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mCenterY).when(mScreenMagnificationController).getCenterY(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mIsRegistered).when(mScreenMagnificationController).isRegistered(
+ TEST_DISPLAY);
+ Answer enableMagnificationStubAnswer = invocation -> {
+ mScale = invocation.getArgument(1);
+ mCenterX = invocation.getArgument(2);
+ mCenterY = invocation.getArgument(3);
+ return true;
+ };
+ doAnswer(enableMagnificationStubAnswer).when(
+ mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(),
+ anyFloat(), anyFloat(), eq(true), eq(SERVICE_ID));
+
+ Answer registerStubAnswer = invocation -> {
+ mIsRegistered = true;
+ return true;
+ };
+ doAnswer(registerStubAnswer).when(
+ mScreenMagnificationController).register(eq(TEST_DISPLAY));
+
+ Answer unregisterStubAnswer = invocation -> {
+ mIsRegistered = false;
+ return true;
+ };
+ doAnswer(unregisterStubAnswer).when(
+ mScreenMagnificationController).unregister(eq(TEST_DISPLAY));
+ }
+
+ public void resetAndStubMethods() {
+ Mockito.reset(mScreenMagnificationController);
+ stubMethods();
+ }
+ }
+
+ private static class WindowMagnificationManagerStub {
+ private final WindowMagnificationManager mWindowMagnificationManager;
+ private float mScale = 1.0f;
+ private float mCenterX = 0;
+ private float mCenterY = 0;
+
+ WindowMagnificationManagerStub(
+ WindowMagnificationManager windowMagnificationManager) {
+ mWindowMagnificationManager = windowMagnificationManager;
+ }
+
+ private void stubMethods() {
+ doAnswer(invocation -> mScale).when(mWindowMagnificationManager).getScale(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mCenterX).when(mWindowMagnificationManager).getCenterX(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mCenterY).when(mWindowMagnificationManager).getCenterY(
+ TEST_DISPLAY);
+ Answer enableWindowMagnificationStubAnswer = invocation -> {
+ mScale = invocation.getArgument(1);
+ mCenterX = invocation.getArgument(2);
+ mCenterY = invocation.getArgument(3);
+ return true;
+ };
+ doAnswer(enableWindowMagnificationStubAnswer).when(
+ mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+ anyFloat(), anyFloat(), anyFloat());
+ }
+
+ public void resetAndStubMethods() {
+ Mockito.reset(mWindowMagnificationManager);
+ stubMethods();
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 8a521d8..ec1a0c2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -38,6 +38,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.accessibilityservice.MagnificationConfig;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -154,7 +155,7 @@
verify(mScreenMagnificationController, never()).reset(anyInt(),
any(MagnificationAnimationCallback.class));
verify(mMockConnection.getConnection(), never()).enableWindowMagnification(anyInt(),
- anyFloat(), anyFloat(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(),
nullable(IRemoteMagnificationAnimationCallback.class));
}
@@ -229,7 +230,7 @@
}
@Test
- public void transitionToFullScreen_centerNotInTheBounds_magnifyTheCenterOfMagnificationBounds()
+ public void transitionToFullScreen_centerNotInTheBounds_magnifyBoundsCenter()
throws RemoteException {
final Rect magnificationBounds = MAGNIFICATION_REGION.getBounds();
final PointF magnifiedCenter = new PointF(magnificationBounds.right + 100,
@@ -289,6 +290,73 @@
}
@Test
+ public void configTransitionToWindowMode_fullScreenMagnifying_disableFullScreenAndEnableWindow()
+ throws RemoteException {
+ activateMagnifier(MODE_FULLSCREEN, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+
+ mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
+ obtainMagnificationConfig(MODE_WINDOW),
+ false);
+
+ verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), eq(false));
+ mMockConnection.invokeCallbacks();
+ assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void configTransitionToFullScreen_windowMagnifying_disableWindowAndEnableFullScreen()
+ throws RemoteException {
+ final boolean animate = true;
+ activateMagnifier(MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+ mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
+ obtainMagnificationConfig(MODE_FULLSCREEN),
+ animate);
+ mMockConnection.invokeCallbacks();
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ animate, MAGNIFICATION_GESTURE_HANDLER_ID);
+ }
+
+ @Test
+ public void configTransitionToFullScreen_userSettingsDisablingFullScreen_enableFullScreen()
+ throws RemoteException {
+ activateMagnifier(MODE_FULLSCREEN, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+ // User-setting mode
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW, mTransitionCallBack);
+
+ // Config-setting mode
+ mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
+ obtainMagnificationConfig(MODE_FULLSCREEN),
+ true);
+
+ assertEquals(DEFAULT_SCALE, mScreenMagnificationController.getScale(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X, mScreenMagnificationController.getCenterX(TEST_DISPLAY),
+ 0);
+ assertEquals(MAGNIFIED_CENTER_Y, mScreenMagnificationController.getCenterY(TEST_DISPLAY),
+ 0);
+ }
+
+ @Test
+ public void interruptDuringTransitionToWindow_disablingFullScreen_discardPreviousTransition()
+ throws RemoteException {
+ activateMagnifier(MODE_FULLSCREEN, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+ // User-setting mode
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW, mTransitionCallBack);
+
+ // Config-setting mode
+ mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
+ obtainMagnificationConfig(MODE_FULLSCREEN),
+ true);
+
+ verify(mTransitionCallBack, never()).onResult(TEST_DISPLAY, true);
+ }
+
+ @Test
public void onDisplayRemoved_notifyAllModules() {
mMagnificationController.onDisplayRemoved(TEST_DISPLAY);
@@ -402,6 +470,16 @@
}
@Test
+ public void onFullScreenMagnificationActivationState_windowActivated_disableMagnification()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
+
+ verify(mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
+ }
+
+ @Test
public void onTouchInteractionStart_fullScreenAndCapabilitiesAll_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
@@ -609,6 +687,10 @@
private void setMagnificationEnabled(int mode, float centerX, float centerY)
throws RemoteException {
setMagnificationModeSettings(mode);
+ activateMagnifier(mode, centerX, centerY);
+ }
+
+ private void activateMagnifier(int mode, float centerX, float centerY) throws RemoteException {
mScreenMagnificationControllerStubber.resetAndStubMethods();
final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
TEST_DISPLAY);
@@ -631,6 +713,11 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, mode, CURRENT_USER_ID);
}
+ private MagnificationConfig obtainMagnificationConfig(int mode) {
+ return new MagnificationConfig.Builder().setMode(mode).setScale(DEFAULT_SCALE).setCenterX(
+ MAGNIFIED_CENTER_X).setCenterY(MAGNIFIED_CENTER_Y).build();
+ }
+
/**
* Stubs public methods to simulate the real beahviours.
*/
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
index 2a53504..0659a60 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
@@ -93,7 +93,7 @@
final float scale = invocation.getArgument(1);
mScale = Float.isNaN(scale) ? mScale : scale;
computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3));
- setAnimationCallback(invocation.getArgument(4));
+ setAnimationCallback(invocation.getArgument(6));
computeSourceBounds();
mHasPendingCallback = true;
if (!mSuspendCallback) {
@@ -101,7 +101,7 @@
}
return null;
}).when(mConnection).enableWindowMagnification(anyInt(), anyFloat(), anyFloat(), anyFloat(),
- nullable(IRemoteMagnificationAnimationCallback.class));
+ anyFloat(), anyFloat(), nullable(IRemoteMagnificationAnimationCallback.class));
}
private void stubDisableWindowMagnification() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index 1638563..3822dc3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -67,7 +67,7 @@
@Test
public void enableWindowMagnification() throws RemoteException {
mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f,
- mAnimationCallback);
+ 0f, 0f, mAnimationCallback);
verify(mAnimationCallback).onResult(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 1b8aff5..b807c11 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -24,6 +24,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
@@ -58,10 +59,11 @@
public static final int STATE_SHOW_MAGNIFIER_SHORTCUT = 2;
public static final int STATE_TWO_FINGERS_DOWN = 3;
public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
+ public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 5;
//TODO: Test it after can injecting Handler to GestureMatcher is available.
public static final int FIRST_STATE = STATE_IDLE;
- public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP;
+ public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_TAP_X = 301;
@@ -178,6 +180,12 @@
== mWindowMagnificationGestureHandler.mDetectingState, state);
}
break;
+ case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ check(isWindowMagnifierEnabled(DISPLAY_0), state);
+ check(mWindowMagnificationGestureHandler.mCurrentState
+ == mWindowMagnificationGestureHandler.mViewportDraggingState, state);
+ }
+ break;
case STATE_TWO_FINGERS_DOWN: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
@@ -229,6 +237,13 @@
tap();
}
break;
+ case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ // Perform triple tap and hold gesture
+ tap();
+ tap();
+ tapAndHold();
+ }
+ break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -262,6 +277,10 @@
tap();
}
break;
+ case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
+ }
+ break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -308,6 +327,11 @@
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
}
+ private void tapAndHold() {
+ send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout() + 100);
+ }
+
private String stateDump() {
return "\nCurrent state dump:\n" + mWindowMagnificationGestureHandler.mCurrentState;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 15ba358..85512f3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -143,8 +143,7 @@
* new connection.
*/
@Test
- public void
- setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
+ public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
MockWindowMagnificationConnection secondConnection =
@@ -177,7 +176,7 @@
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
- eq(200f), eq(300f), notNull());
+ eq(200f), eq(300f), eq(0f), eq(0f), notNull());
}
@Test
@@ -189,7 +188,8 @@
mAnimationCallback);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
- eq(200f), eq(300f), any(IRemoteMagnificationAnimationCallback.class));
+ eq(200f), eq(300f), eq(0f), eq(0f),
+ any(IRemoteMagnificationAnimationCallback.class));
verify(mAnimationCallback).onResult(true);
}
@@ -377,6 +377,17 @@
}
@Test
+ public void resetMagnification_enabled_windowMagnifierDisabled() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+
+ mWindowMagnificationManager.reset(TEST_DISPLAY);
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
throws RemoteException {
mWindowMagnificationManager.requestConnection(true);
@@ -400,6 +411,34 @@
}
@Test
+ public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
+ throws RemoteException {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+
+ assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ eq(100f), eq(200f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
+ throws RemoteException {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
+
+ assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
+ }
+
+ @Test
public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
mWindowMagnificationManager.requestConnection(true);
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
diff --git a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
index 9dd413b..4b359eb 100644
--- a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
@@ -20,6 +20,7 @@
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -30,7 +31,9 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
+import android.os.IBinder;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -50,6 +53,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Build/Install/Run:
@@ -83,6 +88,12 @@
private static final int ACTION_STOPPKG = 8;
private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG;
+ private static final String LOCAL_APK_BASE_PATH = "/data/local/tmp/cts/content/";
+ private static final String TEST_PACKAGE3_APK = "SimpleServiceTestApp3.apk";
+ private static final String ACTION_SERVICE_WITH_DEP_PKG =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
+ private static final String EXTRA_TARGET_PACKAGE = "target_package";
+
private SettingsSession<String> mAMConstantsSettings;
private DeviceConfigSession<String> mExtraDelaysDeviceConfig;
private DeviceConfigSession<Boolean> mEnableExtraDelaysDeviceConfig;
@@ -348,6 +359,83 @@
return res;
}
+ @Test
+ public void testServiceWithDepPkgStopped() throws Exception {
+ final CountDownLatch[] latchHolder = new CountDownLatch[1];
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ latchHolder[0].countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ latchHolder[0].countDown();
+ }
+ };
+
+ final long timeout = 5_000;
+ final long shortTimeout = 2_000;
+ final Intent intent = new Intent(ACTION_SERVICE_WITH_DEP_PKG);
+ final String testPkg = TEST_PACKAGE2_NAME;
+ final String libPkg = TEST_PACKAGE3_NAME;
+ final String apkPath = LOCAL_APK_BASE_PATH + TEST_PACKAGE3_APK;
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+
+ intent.setComponent(ComponentName.unflattenFromString(testPkg + "/" + TEST_SERVICE_NAME));
+ intent.putExtra(EXTRA_TARGET_PACKAGE, libPkg);
+ try {
+ executeShellCmd("am service-restart-backoff disable " + testPkg);
+
+ latchHolder[0] = new CountDownLatch(1);
+ assertTrue("Unable to bind to test service in " + testPkg,
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE));
+ assertTrue("Timed out to bind service in " + testPkg,
+ latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(shortTimeout);
+ assertTrue(libPkg + " should be a dependency package of " + testPkg,
+ isPackageDependency(testPkg, libPkg));
+
+ // Force-stop lib package, the test service should be restarted.
+ latchHolder[0] = new CountDownLatch(2);
+ am.forceStopPackage(libPkg);
+ assertTrue("Test service in didn't restart in " + testPkg,
+ latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(shortTimeout);
+
+ // Re-install the lib package, the test service should be restarted.
+ latchHolder[0] = new CountDownLatch(2);
+ assertTrue("Unable to install package " + apkPath, installPackage(apkPath));
+ assertTrue("Test service in didn't restart in " + testPkg,
+ latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(shortTimeout);
+
+ // Force-stop the service package, the test service should not be restarted.
+ latchHolder[0] = new CountDownLatch(2);
+ am.forceStopPackage(testPkg);
+ assertFalse("Test service should not be restarted in " + testPkg,
+ latchHolder[0].await(timeout * 2, TimeUnit.MILLISECONDS));
+ } finally {
+ executeShellCmd("am service-restart-backoff enable " + testPkg);
+ mContext.unbindService(conn);
+ am.forceStopPackage(testPkg);
+ }
+ }
+
+ private boolean isPackageDependency(String pkgName, String libPackage) throws Exception {
+ final String output = SystemUtil.runShellCommand("dumpsys activity processes " + pkgName);
+ final Matcher matcher = Pattern.compile("packageDependencies=\\{.*?\\b"
+ + libPackage + "\\b.*?\\}").matcher(output);
+ return matcher.find();
+ }
+
+ private boolean installPackage(String apkPath) throws Exception {
+ return executeShellCmd("pm install -t " + apkPath).equals("Success\n");
+ }
+
private void startServiceAndWait(String pkgName, MyUidImportanceListener uidListener,
long timeout) throws Exception {
final Intent intent = new Intent();
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 1c49e6e..e40f543 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -29,7 +29,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.intThat;
+import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -135,7 +135,7 @@
packages.add(makePackageInfo(PACKAGE_NAME_2));
packages.add(makePackageInfo(PACKAGE_NAME_3));
doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
- intThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
+ longThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
UserInfo userInfo = addUser(USER_ID_1);
@@ -412,7 +412,7 @@
UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */);
mUserInfos.add(userInfo);
doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
- .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId));
+ .getInstalledPackages(longThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId));
return userInfo;
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 205ff30..aa7d6aa 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -38,7 +38,7 @@
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.params.BackupParams;
-import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
import org.junit.Before;
@@ -57,7 +57,8 @@
@Mock IBackupManagerMonitor mBackupManagerMonitor;
@Mock IBackupObserver mBackupObserver;
@Mock PackageManager mPackageManager;
- @Mock TransportClient mTransportClient;
+ @Mock
+ TransportConnection mTransportConnection;
@Mock IBackupTransport mBackupTransport;
@Mock BackupEligibilityRules mBackupEligibilityRules;
@@ -96,7 +97,7 @@
BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
- mTransportClient, /* transportDirName */ "", OnTaskFinishedListener.NOP);
+ mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP);
assertThat(params.kvPackages).isEmpty();
assertThat(params.fullPackages).contains(TEST_PACKAGE);
@@ -112,7 +113,7 @@
BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
- mTransportClient, /* transportDirName */ "", OnTaskFinishedListener.NOP);
+ mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP);
assertThat(params.kvPackages).contains(TEST_PACKAGE);
assertThat(params.fullPackages).isEmpty();
@@ -128,7 +129,7 @@
BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver,
mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules,
- mTransportClient, /* transportDirName */ "", OnTaskFinishedListener.NOP);
+ mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP);
assertThat(params.kvPackages).isEmpty();
assertThat(params.fullPackages).isEmpty();
@@ -138,10 +139,10 @@
@Test
public void testGetOperationTypeFromTransport_returnsBackupByDefault()
throws Exception {
- when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport);
+ when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport);
when(mBackupTransport.getTransportFlags()).thenReturn(0);
- int operationType = mService.getOperationTypeFromTransport(mTransportClient);
+ int operationType = mService.getOperationTypeFromTransport(mTransportConnection);
assertThat(operationType).isEqualTo(OperationType.BACKUP);
}
@@ -153,11 +154,11 @@
// rolled out.
mService.shouldUseNewBackupEligibilityRules = true;
- when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport);
+ when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport);
when(mBackupTransport.getTransportFlags()).thenReturn(
BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER);
- int operationType = mService.getOperationTypeFromTransport(mTransportClient);
+ int operationType = mService.getOperationTypeFromTransport(mTransportConnection);
assertThat(operationType).isEqualTo(OperationType.MIGRATION);
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/DelegatingTransportTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/DelegatingTransportTest.java
deleted file mode 100644
index bae11eb..0000000
--- a/services/tests/servicestests/src/com/android/server/backup/transport/DelegatingTransportTest.java
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.transport;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.backup.RestoreDescription;
-import android.app.backup.RestoreSet;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.backup.IBackupTransport;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class DelegatingTransportTest {
- @Mock private IBackupTransport mBackupTransport;
- @Mock private PackageInfo mPackageInfo;
- @Mock private ParcelFileDescriptor mFd;
-
- private final String mPackageName = "testpackage";
- private final RestoreSet mRestoreSet = new RestoreSet();
- private final int mFlags = 1;
- private final long mRestoreToken = 10;
- private final long mSize = 100;
- private final int mNumBytes = 1000;
- private DelegatingTransport mDelegatingTransport;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mDelegatingTransport = new DelegatingTransport() {
- @Override
- protected IBackupTransport getDelegate() {
- return mBackupTransport;
- }
- };
- }
-
- @Test
- public void testName() throws RemoteException {
- String exp = "dummy";
- when(mBackupTransport.name()).thenReturn(exp);
-
- String ret = mDelegatingTransport.name();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).name();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testConfigurationIntent() throws RemoteException {
- Intent exp = new Intent("dummy");
- when(mBackupTransport.configurationIntent()).thenReturn(exp);
-
- Intent ret = mDelegatingTransport.configurationIntent();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).configurationIntent();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testCurrentDestinationString() throws RemoteException {
- String exp = "dummy";
- when(mBackupTransport.currentDestinationString()).thenReturn(exp);
-
- String ret = mDelegatingTransport.currentDestinationString();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).currentDestinationString();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testDataManagementIntent() throws RemoteException {
- Intent exp = new Intent("dummy");
- when(mBackupTransport.dataManagementIntent()).thenReturn(exp);
-
- Intent ret = mDelegatingTransport.dataManagementIntent();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).dataManagementIntent();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testDataManagementIntentLabel() throws RemoteException {
- String exp = "dummy";
- when(mBackupTransport.dataManagementIntentLabel()).thenReturn(exp);
-
- CharSequence ret = mDelegatingTransport.dataManagementIntentLabel();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).dataManagementIntentLabel();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testTransportDirName() throws RemoteException {
- String exp = "dummy";
- when(mBackupTransport.transportDirName()).thenReturn(exp);
-
- String ret = mDelegatingTransport.transportDirName();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).transportDirName();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testRequestBackupTime() throws RemoteException {
- long exp = 1000L;
- when(mBackupTransport.requestBackupTime()).thenReturn(exp);
-
- long ret = mDelegatingTransport.requestBackupTime();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).requestBackupTime();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testInitializeDevice() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.initializeDevice()).thenReturn(exp);
-
- long ret = mDelegatingTransport.initializeDevice();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).initializeDevice();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testPerformBackup() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.performBackup(mPackageInfo, mFd, mFlags)).thenReturn(exp);
-
- int ret = mDelegatingTransport.performBackup(mPackageInfo, mFd, mFlags);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).performBackup(mPackageInfo, mFd, mFlags);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testClearBackupData() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.clearBackupData(mPackageInfo)).thenReturn(exp);
-
- int ret = mDelegatingTransport.clearBackupData(mPackageInfo);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).clearBackupData(mPackageInfo);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testFinishBackup() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.finishBackup()).thenReturn(exp);
-
- int ret = mDelegatingTransport.finishBackup();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).finishBackup();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testGetAvailableRestoreSets() throws RemoteException {
- RestoreSet[] exp = new RestoreSet[] {mRestoreSet};
- when(mBackupTransport.getAvailableRestoreSets()).thenReturn(exp);
-
- RestoreSet[] ret = mDelegatingTransport.getAvailableRestoreSets();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).getAvailableRestoreSets();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testGetCurrentRestoreSet() throws RemoteException {
- long exp = 1000;
- when(mBackupTransport.getCurrentRestoreSet()).thenReturn(exp);
-
- long ret = mDelegatingTransport.getCurrentRestoreSet();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).getCurrentRestoreSet();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testStartRestore() throws RemoteException {
- int exp = 1000;
- PackageInfo[] packageInfos = {mPackageInfo};
- when(mBackupTransport.startRestore(mRestoreToken, packageInfos)).thenReturn(exp);
-
- int ret = mDelegatingTransport.startRestore(mRestoreToken, packageInfos);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).startRestore(mRestoreToken, packageInfos);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testNextRestorePackage() throws RemoteException {
- RestoreDescription exp = new RestoreDescription(mPackageName, 1);
- when(mBackupTransport.nextRestorePackage()).thenReturn(exp);
-
- RestoreDescription ret = mDelegatingTransport.nextRestorePackage();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).nextRestorePackage();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testGetRestoreData() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.getRestoreData(mFd)).thenReturn(exp);
-
- int ret = mDelegatingTransport.getRestoreData(mFd);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).getRestoreData(mFd);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void tesFinishRestore() throws RemoteException {
- mDelegatingTransport.finishRestore();
-
- verify(mBackupTransport, times(1)).finishRestore();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testRequestFullBackupTime() throws RemoteException {
- long exp = 1000L;
- when(mBackupTransport.requestFullBackupTime()).thenReturn(exp);
-
- long ret = mDelegatingTransport.requestFullBackupTime();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).requestFullBackupTime();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testPerformFullBackup() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.performFullBackup(mPackageInfo, mFd, mFlags)).thenReturn(exp);
-
- int ret = mDelegatingTransport.performFullBackup(mPackageInfo, mFd, mFlags);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).performFullBackup(mPackageInfo, mFd, mFlags);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testCheckFullBackupSize() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.checkFullBackupSize(mSize)).thenReturn(exp);
-
- int ret = mDelegatingTransport.checkFullBackupSize(mSize);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).checkFullBackupSize(mSize);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testSendBackupData() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.sendBackupData(mNumBytes)).thenReturn(exp);
-
- int ret = mDelegatingTransport.sendBackupData(mNumBytes);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).sendBackupData(mNumBytes);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testCancelFullBackup() throws RemoteException {
- mDelegatingTransport.cancelFullBackup();
-
- verify(mBackupTransport, times(1)).cancelFullBackup();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testIsAppEligibleForBackup() throws RemoteException {
- boolean exp = true;
- when(mBackupTransport.isAppEligibleForBackup(mPackageInfo, true)).thenReturn(exp);
-
- boolean ret = mDelegatingTransport.isAppEligibleForBackup(mPackageInfo, true);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).isAppEligibleForBackup(mPackageInfo, true);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testGetBackupQuota() throws RemoteException {
- long exp = 1000;
- when(mBackupTransport.getBackupQuota(mPackageName, true)).thenReturn(exp);
-
- long ret = mDelegatingTransport.getBackupQuota(mPackageName, true);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).getBackupQuota(mPackageName, true);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testGetNextFullRestoreDataChunk() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.getNextFullRestoreDataChunk(mFd)).thenReturn(exp);
-
- int ret = mDelegatingTransport.getNextFullRestoreDataChunk(mFd);
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).getNextFullRestoreDataChunk(mFd);
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testAbortFullRestore() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.abortFullRestore()).thenReturn(exp);
-
- int ret = mDelegatingTransport.abortFullRestore();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).abortFullRestore();
- verifyNoMoreInteractions(mBackupTransport);
- }
-
- @Test
- public void testGetTransportFlags() throws RemoteException {
- int exp = 1000;
- when(mBackupTransport.getTransportFlags()).thenReturn(exp);
-
- int ret = mDelegatingTransport.getTransportFlags();
-
- assertEquals(exp, ret);
- verify(mBackupTransport, times(1)).getTransportFlags();
- verifyNoMoreInteractions(mBackupTransport);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index e3e3900..d192697 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -143,8 +143,8 @@
final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
- final BiometricPromptClientMonitor client1 =
- new BiometricPromptClientMonitor(mContext, mToken, lazyDaemon1, listener1);
+ final TestAuthenticationClient client1 =
+ new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
@@ -180,8 +180,8 @@
@Test
public void testCancelNotInvoked_whenOperationWaitingForCookie() {
final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class);
- final BiometricPromptClientMonitor client1 = new BiometricPromptClientMonitor(mContext,
- mToken, lazyDaemon1, mock(ClientMonitorCallbackConverter.class));
+ final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
+ lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class));
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
// Schedule a BiometricPrompt authentication request
@@ -195,6 +195,8 @@
// should go back to idle, since in this case the framework has not even requested the HAL
// to authenticate yet.
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+ assertTrue(client1.isAlreadyDone());
+ assertTrue(client1.mDestroyed);
assertNull(mScheduler.mCurrentOperation);
}
@@ -316,6 +318,10 @@
eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
eq(0) /* vendorCode */);
assertNull(mScheduler.getCurrentClient());
+ assertTrue(client1.isAlreadyDone());
+ assertTrue(client1.mDestroyed);
+ assertTrue(client2.isAlreadyDone());
+ assertTrue(client2.mDestroyed);
}
@Test
@@ -465,39 +471,9 @@
return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
}
- private static class BiometricPromptClientMonitor extends AuthenticationClient<Object> {
-
- public BiometricPromptClientMonitor(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, ClientMonitorCallbackConverter listener) {
- super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
- false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
- TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */,
- 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class),
- false /* isKeyguard */, true /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
- }
-
- @Override
- protected void stopHalOperation() {
- }
-
- @Override
- protected void startHalOperation() {
- }
-
- @Override
- protected void handleLifecycleAfterAuth(boolean authenticated) {
-
- }
-
- @Override
- public boolean wasUserDetected() {
- return false;
- }
- }
-
private static class TestAuthenticationClient extends AuthenticationClient<Object> {
int mNumCancels = 0;
+ boolean mDestroyed = false;
public TestAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -530,6 +506,13 @@
return false;
}
+ @Override
+ public void destroy() {
+ mDestroyed = true;
+ super.destroy();
+ }
+
+ @Override
public void cancel() {
mNumCancels++;
super.cancel();
@@ -595,6 +578,7 @@
@Override
public void destroy() {
+ super.destroy();
mDestroyed = true;
}
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 2777bdf..3c809f9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1735,11 +1735,11 @@
pi.applicationInfo.flags = flags;
doReturn(pi).when(getServices().ipackageManager).getPackageInfo(
eq(packageName),
- anyInt(),
+ anyLong(),
eq(userId));
doReturn(pi.applicationInfo).when(getServices().ipackageManager).getApplicationInfo(
eq(packageName),
- anyInt(),
+ anyLong(),
eq(userId));
doReturn(true).when(getServices().ipackageManager).isPackageAvailable(packageName, userId);
// Setup application UID with the PackageManager
@@ -4708,11 +4708,11 @@
// Ensure packages are *not* flagged as test_only.
doReturn(new ApplicationInfo()).when(getServices().ipackageManager).getApplicationInfo(
eq(admin1.getPackageName()),
- anyInt(),
+ anyLong(),
eq(CALLER_USER_HANDLE));
doReturn(new ApplicationInfo()).when(getServices().ipackageManager).getApplicationInfo(
eq(admin2.getPackageName()),
- anyInt(),
+ anyLong(),
eq(CALLER_USER_HANDLE));
// Initial state is disabled.
@@ -7078,7 +7078,7 @@
doReturn(ai).when(getServices().ipackageManager).getApplicationInfo(
eq(admin1.getPackageName()),
- anyInt(),
+ anyLong(),
eq(CALLER_USER_HANDLE));
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index fe0df58..b8824c3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -21,7 +21,6 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
@@ -221,7 +220,7 @@
doReturn(ai).when(mServices.ipackageManager).getApplicationInfo(
eq(admin.getPackageName()),
- anyInt(),
+ anyLong(),
eq(UserHandle.getUserId(packageUid)));
// Set up queryBroadcastReceivers().
@@ -248,7 +247,7 @@
doReturn(aci).when(mServices.ipackageManager).getReceiverInfo(
eq(admin),
- anyInt(),
+ anyLong(),
eq(UserHandle.getUserId(packageUid)));
doReturn(new String[] {admin.getPackageName()}).when(mServices.ipackageManager)
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 285806b..c675726 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -59,6 +59,20 @@
5000
};
+ private static final int[] LUX_LEVELS_IDLE = {
+ 0,
+ 10,
+ 40,
+ 80,
+ 200,
+ 655,
+ 1200,
+ 2500,
+ 4400,
+ 8000,
+ 10000
+ };
+
private static final float[] DISPLAY_LEVELS_NITS = {
13.25f,
54.0f,
@@ -73,6 +87,20 @@
478.5f,
};
+ private static final float[] DISPLAY_LEVELS_NITS_IDLE = {
+ 23.25f,
+ 64.0f,
+ 88.85f,
+ 115.02f,
+ 142.7f,
+ 180.12f,
+ 222.1f,
+ 275.2f,
+ 345.8f,
+ 425.2f,
+ 468.5f,
+ };
+
private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
9,
30,
@@ -88,7 +116,6 @@
};
private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
- private static final float[] DISPLAY_LEVELS_RANGE_NITS = { 13.25f, 478.5f };
private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
private static final float[] DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT = { 0.03149606299f, 1.0f };
@@ -118,6 +145,8 @@
new float[] { 0.0f, 100.0f, 1000.0f, 2500.0f, 4000.0f, 4900.0f, 5000.0f },
new float[] { 0.0475f, 0.0475f, 0.2225f, 0.5140f, 0.8056f, 0.9805f, 1.0f });
+ private static final float TOLERANCE = 0.0001f;
+
@Test
public void testSimpleStrategyMappingAtControlPoints() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
@@ -357,6 +386,27 @@
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc));
}
+ @Test
+ public void testIdleModeConfigLoadsCorrectly() {
+ Resources res = createResourcesIdle(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
+
+ // Create an idle mode bms
+ // This will fail if it tries to fetch the wrong configuration.
+ BrightnessMappingStrategy bms = BrightnessMappingStrategy.createForIdleMode(res, ddc);
+ assertNotNull("BrightnessMappingStrategy should not be null", bms);
+
+ // Ensure that the config is the one we set
+ // Ensure that the lux -> brightness -> nits path works. ()
+ for (int i = 0; i < DISPLAY_LEVELS_NITS_IDLE.length; i++) {
+ assertEquals(LUX_LEVELS_IDLE[i], bms.getDefaultConfig().getCurve().first[i], TOLERANCE);
+ assertEquals(DISPLAY_LEVELS_NITS_IDLE[i], bms.getDefaultConfig().getCurve().second[i],
+ TOLERANCE);
+ assertEquals(bms.convertToNits(bms.getBrightness(LUX_LEVELS_IDLE[i])),
+ DISPLAY_LEVELS_NITS_IDLE[i], TOLERANCE);
+ }
+ }
+
private static void assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy strategy) {
// Save out all of the initial brightness data for comparison after reset.
float[] initialBrightnessLevels = new float[LUX_LEVELS.length];
@@ -421,14 +471,39 @@
brightnessLevelsNits);
}
+ private Resources createResourcesIdle(int[] luxLevels, float[] brightnessLevelsNits) {
+ return createResources(EMPTY_INT_ARRAY, EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY,
+ luxLevels, brightnessLevelsNits);
+ }
+
private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight,
float[] brightnessLevelsNits) {
+ return createResources(luxLevels, brightnessLevelsBacklight, brightnessLevelsNits,
+ EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY);
+
+ }
+
+ private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight,
+ float[] brightnessLevelsNits, int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) {
+
Resources mockResources = mock(Resources.class);
+
// For historical reasons, the lux levels resource implicitly defines the first point as 0,
// so we need to chop it off of the array the mock resource object returns.
- int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length);
- when(mockResources.getIntArray(com.android.internal.R.array.config_autoBrightnessLevels))
- .thenReturn(luxLevelsResource);
+ // Don't mock if these values are not set. If we try to use them, we will fail.
+ if (luxLevels.length > 0) {
+ int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length);
+ when(mockResources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevels))
+ .thenReturn(luxLevelsResource);
+ }
+ if (luxLevelsIdle.length > 0) {
+ int[] luxLevelsIdleResource = Arrays.copyOfRange(luxLevelsIdle, 1,
+ luxLevelsIdle.length);
+ when(mockResources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevelsIdle))
+ .thenReturn(luxLevelsIdleResource);
+ }
when(mockResources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
@@ -438,6 +513,10 @@
when(mockResources.obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
.thenReturn(mockBrightnessLevelNits);
+ TypedArray mockBrightnessLevelNitsIdle = createFloatTypedArray(brightnessLevelsNitsIdle);
+ when(mockResources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle))
+ .thenReturn(mockBrightnessLevelNitsIdle);
when(mockResources.getInteger(
com.android.internal.R.integer.config_screenBrightnessSettingMinimum))
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index fcfe273..68e90fb 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -16,20 +16,27 @@
package com.android.server.display;
+import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
+import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
+
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
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.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.PropertyInvalidatedCache;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.display.BrightnessConfiguration;
@@ -48,6 +55,8 @@
import android.os.MessageQueue;
import android.os.Process;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
@@ -55,7 +64,9 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -65,6 +76,7 @@
import com.android.server.display.DisplayManagerService.SyncRoot;
import com.android.server.lights.LightsManager;
import com.android.server.sensors.SensorManagerInternal;
+import com.android.server.testutils.FakeDeviceConfigInterface;
import com.android.server.wm.WindowManagerInternal;
import com.google.common.collect.ImmutableMap;
@@ -105,6 +117,7 @@
public TestRule compatChangeRule = new PlatformCompatChangeRule();
private Context mContext;
+ private FakeDeviceConfigInterface mDeviceConfig;
private final DisplayManagerService.Injector mShortMockedInjector =
new DisplayManagerService.Injector() {
@@ -127,6 +140,12 @@
return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
(String name, boolean secure) -> mMockDisplayToken);
}
+
+ @NonNull
+ @Override
+ public DeviceConfigInterface getDeviceConfig() {
+ return mDeviceConfig;
+ }
}
private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
@@ -169,7 +188,8 @@
LocalServices.removeServiceForTest(SensorManagerInternal.class);
LocalServices.addService(SensorManagerInternal.class, mMockSensorManagerInternal);
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mDeviceConfig = new FakeDeviceConfigInterface();
// Disable binder caches in this process.
PropertyInvalidatedCache.disableForTestMode();
@@ -619,6 +639,25 @@
}
/**
+ * Tests that specifying the VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED flag is processed correctly
+ * when it is allowed by DeviceConfig.
+ */
+ @Test
+ public void testCreateVirtualDisplay_alwaysUnlockedAllowed() {
+ testCreateVirtualDisplay_alwaysUnlocked(/*deviceConfigAllows*/ true, /*flagExpected*/ true);
+ }
+
+ /**
+ * Tests that specifying the VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED flag when DeviceConfig does
+ * not allow it results in the flag being stripped from the final flags.
+ */
+ @Test
+ public void testCreateVirtualDisplay_alwaysUnlockedDisallowed() {
+ testCreateVirtualDisplay_alwaysUnlocked(
+ /*deviceConfigAllows*/ false, /*flagExpected*/ false);
+ }
+
+ /**
* Tests that there is a display change notification if the frame rate override
* list is updated.
*/
@@ -1042,6 +1081,45 @@
assertEquals(expectedRefreshRate, displayInfo.getRefreshRate(), 0.01f);
}
+ private void testCreateVirtualDisplay_alwaysUnlocked(boolean deviceConfigAllows,
+ boolean flagExpected) {
+ mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_ALLOW_ALWAYS_UNLOCKED_VIRTUAL_DISPLAYS,
+ deviceConfigAllows ? "true" : "false", /*makeDefault*/ false);
+
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ String uniqueId = "uniqueId --- ALWAYS_UNLOCKED";
+ int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
+
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+ when(mContext.checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY)).thenReturn(
+ PackageManager.PERMISSION_GRANTED);
+ when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn(
+ PackageManager.PERMISSION_GRANTED);
+
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setFlags(flags);
+ builder.setUniqueId(uniqueId);
+
+ int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
+ null /* projection */, PACKAGE_NAME);
+ displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
+ DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
+ assertNotNull(ddi);
+ if (flagExpected) {
+ assertNotEquals(ddi.flags & DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED, 0);
+ } else {
+ assertEquals(ddi.flags & DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED, 0);
+ }
+ }
+
private int getDisplayIdForDisplayDevice(
DisplayManagerService displayManager,
DisplayManagerService.BinderService displayManagerBinderService,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index cf4bdf6..b588db6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -16,6 +16,7 @@
package com.android.server.hdmi;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -153,6 +154,7 @@
mHdmiControlService);
audioDevice.init();
mLocalDevices.add(audioDevice);
+ mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 4ff7c669..ff01cb1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.hdmi;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.google.common.truth.Truth.assertThat;
@@ -115,6 +116,7 @@
mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+ hdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
hdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index c6bb914..a44a5cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.hdmi;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.google.common.truth.Truth.assertThat;
@@ -114,6 +115,7 @@
mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+ hdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
hdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerInternalWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerInternalWrapper.java
new file mode 100644
index 0000000..968a75c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerInternalWrapper.java
@@ -0,0 +1,37 @@
+/*
+ * 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.server.hdmi;
+
+/**
+ * Fake class which stubs PowerManagerInternalWrapper (useful for testing).
+ */
+public class FakePowerManagerInternalWrapper extends PowerManagerInternalWrapper {
+
+ private long mIdleDurationMs = -1;
+
+ /**
+ * Sets the duration (in milliseconds) that this device has been idle for.
+ */
+ public void setIdleDuration(long idleDurationMs) {
+ mIdleDurationMs = idleDurationMs;
+ }
+
+ @Override
+ public boolean wasDeviceIdleFor(long ms) {
+ return ms <= mIdleDurationMs;
+ }
+}
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 17f827d..a411392 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.hdmi;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
@@ -206,6 +207,7 @@
4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlService.initService();
+ mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
// No TV device interacts with AVR so system audio control won't be turned on here
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 055459c..2d13e69 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -76,6 +76,8 @@
private int mPlaybackLogicalAddress;
private boolean mWokenUp;
private boolean mActiveMediaSessionsPaused;
+ private FakePowerManagerInternalWrapper mPowerManagerInternal =
+ new FakePowerManagerInternalWrapper();
@Before
public void setUp() {
@@ -146,6 +148,7 @@
mHdmiControlService.initService();
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
+ mHdmiControlService.setPowerManagerInternal(mPowerManagerInternal);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPlaybackPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
@@ -1969,4 +1972,41 @@
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortPressed);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortReleased);
}
+
+ @Test
+ public void onHotplugInAfterHotplugOut_noStandbyAfterDelay() {
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.onHotplugEvent(1, false);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(
+ HdmiCecLocalDevicePlayback.STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS / 2);
+ mNativeWrapper.onHotplugEvent(1, true);
+ mTestLooper.dispatchAll();
+
+ mPowerManagerInternal.setIdleDuration(
+ HdmiCecLocalDevicePlayback.STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS);
+ mTestLooper.moveTimeForward(HdmiCecLocalDevicePlayback.STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mPowerManager.isInteractive()).isTrue();
+ }
+
+ @Test
+ public void onHotplugOut_standbyAfterDelay_onlyAfterDeviceIdle() {
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.onHotplugEvent(1, false);
+ mTestLooper.dispatchAll();
+
+ mPowerManagerInternal.setIdleDuration(0);
+ mTestLooper.moveTimeForward(HdmiCecLocalDevicePlayback.STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mPowerManager.isInteractive()).isTrue();
+
+ mPowerManagerInternal.setIdleDuration(
+ HdmiCecLocalDevicePlayback.STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS);
+ mTestLooper.moveTimeForward(HdmiCecLocalDevicePlayback.STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mPowerManager.isInteractive()).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index b3a513f..ddc58b2 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -23,6 +23,7 @@
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.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -110,7 +111,7 @@
@Test(expected = SecurityException.class)
public void testSetApplicationLocales_arbitraryAppWithoutPermissions_fails() throws Exception {
doReturn(DEFAULT_UID)
- .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyInt(), anyInt());
+ .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyLong(), anyInt());
setUpFailingPermissionCheckFor(Manifest.permission.CHANGE_CONFIGURATION);
try {
@@ -153,7 +154,7 @@
@Test
public void testSetApplicationLocales_arbitraryAppWithPermission_succeeds() throws Exception {
doReturn(DEFAULT_UID)
- .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyInt(), anyInt());
+ .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyLong(), anyInt());
// if package is not owned by the caller, the calling app should have the following
// permission. We will mock this to succeed to imitate that.
setUpPassingPermissionCheckFor(Manifest.permission.CHANGE_CONFIGURATION);
@@ -168,7 +169,7 @@
@Test
public void testSetApplicationLocales_callerOwnsPackage_succeeds() throws Exception {
doReturn(Binder.getCallingUid())
- .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyInt(), anyInt());
+ .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyLong(), anyInt());
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
DEFAULT_LOCALES);
@@ -179,7 +180,7 @@
@Test(expected = IllegalArgumentException.class)
public void testSetApplicationLocales_invalidPackageOrUserId_fails() throws Exception {
doReturn(INVALID_UID)
- .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyInt(), anyInt());
+ .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyLong(), anyInt());
try {
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
LocaleList.getEmptyLocaleList());
@@ -192,7 +193,7 @@
@Test(expected = SecurityException.class)
public void testGetApplicationLocales_arbitraryAppWithoutPermission_fails() throws Exception {
doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
- .getPackageUid(anyString(), anyInt(), anyInt());
+ .getPackageUid(anyString(), anyLong(), anyInt());
setUpFailingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
try {
@@ -210,7 +211,7 @@
throws Exception {
// any valid app calling for its own package or having appropriate permission
doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
- .getPackageUid(anyString(), anyInt(), anyInt());
+ .getPackageUid(anyString(), anyLong(), anyInt());
setUpPassingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
doReturn(null)
.when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
@@ -225,7 +226,7 @@
public void testGetApplicationLocales_appSpecificLocalesAbsent_returnsEmptyList()
throws Exception {
doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
- .getPackageUid(anyString(), anyInt(), anyInt());
+ .getPackageUid(anyString(), anyLong(), anyInt());
setUpPassingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
doReturn(new PackageConfig(/* nightMode = */ 0, /* locales = */ null))
.when(mMockActivityTaskManager).getApplicationConfig(any(), anyInt());
@@ -240,7 +241,7 @@
public void testGetApplicationLocales_callerOwnsAppAndConfigPresent_returnsLocales()
throws Exception {
doReturn(Binder.getCallingUid()).when(mMockPackageManagerInternal)
- .getPackageUid(anyString(), anyInt(), anyInt());
+ .getPackageUid(anyString(), anyLong(), anyInt());
doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES))
.when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
@@ -254,7 +255,7 @@
public void testGetApplicationLocales_arbitraryCallerWithPermissions_returnsLocales()
throws Exception {
doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
- .getPackageUid(anyString(), anyInt(), anyInt());
+ .getPackageUid(anyString(), anyLong(), anyInt());
setUpPassingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES))
.when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index f45c869..3722ba4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -27,6 +27,7 @@
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
@@ -2357,7 +2358,7 @@
protected void prepareIntentActivities(ComponentName cn) {
when(mMockPackageManagerInternal.queryIntentActivities(
- anyOrNull(Intent.class), anyStringOrNull(), anyInt(), anyInt(), anyInt()))
+ anyOrNull(Intent.class), anyStringOrNull(), anyLong(), anyInt(), anyInt()))
.thenReturn(Collections.singletonList(
ri(cn.getPackageName(), cn.getClassName(), false, 0)));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
index 764c504..6245f82 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
@@ -22,8 +22,9 @@
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.BundleUtils;
@@ -35,6 +36,7 @@
* Build/Install/Run:
* atest com.android.server.pm.BundleUtilsTest
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BundleUtilsTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
index b228c83..54ab133 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
@@ -32,6 +32,7 @@
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.pkg.PackageUserStateImpl;
@@ -40,6 +41,7 @@
import org.junit.Before;
import org.junit.Test;
+@Presubmit
public class CompatibilityModeTest {
private boolean mCompatibilityModeEnabled;;
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index e811c1f..3cb5d5f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -3,9 +3,10 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
@@ -14,7 +15,6 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-
import static org.testng.Assert.assertThrows;
import android.Manifest;
@@ -581,7 +581,7 @@
private void mockAppsInstalled(String packageName, int user, boolean installed) {
when(mPackageManagerInternal.getPackageInfo(
eq(packageName),
- anyInt(),
+ anyLong(),
anyInt(),
eq(user)))
.thenReturn(installed ? createInstalledPackageInfo() : null);
@@ -604,7 +604,7 @@
mActivityInfo = activityInfo;
when(mPackageManagerInternal.queryIntentActivities(
- any(Intent.class), nullable(String.class), anyInt(), anyInt(), anyInt()))
+ any(Intent.class), nullable(String.class), anyLong(), anyInt(), anyInt()))
.thenReturn(Collections.singletonList(resolveInfo));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 9631863..6b6d84a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -20,6 +20,7 @@
import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
import android.content.pm.Signature;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -33,6 +34,7 @@
import java.security.PublicKey;
import java.security.cert.CertificateException;
+@Presubmit
public class KeySetManagerServiceTest extends AndroidTestCase {
private WatchedArrayMap<String, PackageSetting> mPackagesMap;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
index 6a9ef8a..9ea7907 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
import android.test.InstrumentationTestCase;
import com.android.frameworks.servicestests.R;
@@ -30,6 +31,7 @@
import java.util.Collections;
import java.util.List;
+@Presubmit
public class ModuleInfoProviderTest extends InstrumentationTestCase {
@Mock private ApexManager mApexManager;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 7aea392..b81a4ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
@@ -27,11 +29,18 @@
import static java.lang.reflect.Modifier.isStatic;
import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.content.IIntentReceiver;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Postsubmit;
import android.util.SparseArray;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.HexDump;
@@ -42,6 +51,7 @@
import org.junit.After;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,10 +70,21 @@
// atest PackageManagerServiceTest
// runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services
// bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest
+@Postsubmit
@RunWith(AndroidJUnit4.class)
public class PackageManagerServiceTest {
+
+ private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
+
+ private static final String TEST_DATA_PATH = "/data/local/tmp/servicestests/";
+ private static final String TEST_APP_APK = "StubTestApp.apk";
+ private static final String TEST_PKG_NAME = "com.android.servicestests.apps.stubapp";
+
+ private IPackageManager mIPackageManager;
+
@Before
public void setUp() throws Exception {
+ mIPackageManager = AppGlobals.getPackageManager();
}
@After
@@ -603,4 +624,138 @@
Collections.sort(knownPackageIds);
return knownPackageIds;
}
+
+ @Test
+ public void testInstallReason_afterUpdate_keepUnchanged() throws Exception {
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ try {
+ // Try to install test APK with reason INSTALL_REASON_POLICY
+ runShellCommand("pm install --install-reason 1 " + testApk);
+ assertWithMessage("The install reason of test APK is incorrect.").that(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME,
+ UserHandle.myUserId())).isEqualTo(PackageManager.INSTALL_REASON_POLICY);
+
+ // Try to update test APK with different reason INSTALL_REASON_USER
+ runShellCommand("pm install --install-reason 4 " + testApk);
+ assertWithMessage("The install reason should keep unchanged after update.").that(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME,
+ UserHandle.myUserId())).isEqualTo(PackageManager.INSTALL_REASON_POLICY);
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ }
+ }
+
+ @Test
+ public void testInstallReason_userRemainsUninstalled_keepUnknown() throws Exception {
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+ final UserManager um = UserManager.get(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ int userId = UserHandle.USER_NULL;
+ try {
+ // Try to install test APK with reason INSTALL_REASON_POLICY
+ runShellCommand("pm install --install-reason 1 " + testApk);
+ assertWithMessage("The install reason of test APK is incorrect.").that(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME,
+ UserHandle.myUserId())).isEqualTo(PackageManager.INSTALL_REASON_POLICY);
+
+ // Create and start the 2nd user.
+ userId = um.createUser("Test User", 0 /* flags */).getUserHandle().getIdentifier();
+ runShellCommand("am start-user -w " + userId);
+ // Since the test APK isn't installed on the 2nd user, the reason should be unknown.
+ assertWithMessage("The install reason in 2nd user should be unknown.").that(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME, userId)).isEqualTo(
+ PackageManager.INSTALL_REASON_UNKNOWN);
+
+ // Try to update test APK with different reason INSTALL_REASON_USER
+ runShellCommand("pm install --install-reason 4 " + testApk);
+ assertWithMessage("The install reason in 2nd user should keep unknown.").that(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME, userId)).isEqualTo(
+ PackageManager.INSTALL_REASON_UNKNOWN);
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ if (userId != UserHandle.USER_NULL) {
+ um.removeUser(userId);
+ }
+ }
+ }
+
+ @Test
+ public void testInstallReason_installForAllUsers_sameReason() throws Exception {
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+ final UserManager um = UserManager.get(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ int userId = UserHandle.USER_NULL;
+ try {
+ // Create and start the 2nd user.
+ userId = um.createUser("Test User", 0 /* flags */).getUserHandle().getIdentifier();
+ runShellCommand("am start-user -w " + userId);
+
+ // Try to install test APK to all users with reason INSTALL_REASON_POLICY
+ runShellCommand("pm install --install-reason 1 " + testApk);
+ assertWithMessage("The install reason is inconsistent across users.").that(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME,
+ UserHandle.myUserId())).isEqualTo(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME, userId));
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ if (userId != UserHandle.USER_NULL) {
+ um.removeUser(userId);
+ }
+ }
+ }
+
+ @Test
+ public void testInstallReason_installSeparately_withSeparatedReason() throws Exception {
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+ final UserManager um = UserManager.get(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ int userId = UserHandle.USER_NULL;
+ try {
+ // Create and start the 2nd user.
+ userId = um.createUser("Test User", 0 /* flags */).getUserHandle().getIdentifier();
+ runShellCommand("am start-user -w " + userId);
+
+ // Try to install test APK on the current user with reason INSTALL_REASON_POLICY
+ runShellCommand("pm install --user cur --install-reason 1 " + testApk);
+ assertWithMessage("The install reason on the current user is incorrect.").that(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME,
+ UserHandle.myUserId())).isEqualTo(PackageManager.INSTALL_REASON_POLICY);
+
+ // Try to install test APK on the 2nd user with reason INSTALL_REASON_USER
+ runShellCommand("pm install --user " + userId + " --install-reason 4 " + testApk);
+ assertWithMessage("The install reason on the 2nd user is incorrect.").that(
+ mIPackageManager.getInstallReason(TEST_PKG_NAME, userId)).isEqualTo(
+ PackageManager.INSTALL_REASON_USER);
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ if (userId != UserHandle.USER_NULL) {
+ um.removeUser(userId);
+ }
+ }
+ }
+
+ @Test
+ public void testSetSplashScreenTheme_samePackage_succeeds() throws Exception {
+ mIPackageManager.setSplashScreenTheme(PACKAGE_NAME, null /* themeName */,
+ UserHandle.myUserId());
+ // Invoking setSplashScreenTheme on the same package shouldn't get any exception.
+ }
+
+ @Test
+ public void testSetSplashScreenTheme_differentPackage_fails() throws Exception {
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ try {
+ runShellCommand("pm install " + testApk);
+ mIPackageManager.setSplashScreenTheme(TEST_PKG_NAME, null /* themeName */,
+ UserHandle.myUserId());
+ fail("setSplashScreenTheme did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 5d93e3d..6c9f8fe 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -25,6 +25,7 @@
import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
import static android.content.res.Resources.ID_NULL;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -46,6 +47,7 @@
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
@@ -86,11 +88,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageManagerSettingsTests {
@@ -496,6 +498,71 @@
}
@Test
+ public void testWriteReadUsesSdkLibraries() {
+ final Settings settingsUnderTest = makeSettings();
+ final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+ ps1.setAppId(Process.FIRST_APPLICATION_UID);
+ ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
+ .setUid(ps1.getAppId())
+ .setSystem(true)
+ .hideAsFinal());
+ final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+ ps2.setAppId(Process.FIRST_APPLICATION_UID + 1);
+ ps2.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_2).hideAsParsed())
+ .setUid(ps2.getAppId())
+ .hideAsFinal());
+
+ ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" });
+ ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 });
+ ps1.setFlags(ps1.getFlags() | ApplicationInfo.FLAG_SYSTEM);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
+ assertThat(settingsUnderTest.disableSystemPackageLPw(PACKAGE_NAME_1, false), is(true));
+
+ ps2.setUsesSdkLibraries(new String[] { "com.example.sdk.two" });
+ ps2.setUsesSdkLibrariesVersionsMajor(new long[] { 34 });
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
+
+ settingsUnderTest.writeLPr();
+
+ settingsUnderTest.mPackages.clear();
+ settingsUnderTest.mDisabledSysPackages.clear();
+
+ assertThat(settingsUnderTest.readLPw(createFakeUsers()), is(true));
+
+ PackageSetting readPs1 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_1);
+ PackageSetting readPs2 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_2);
+
+ Truth.assertThat(readPs1).isNotNull();
+ Truth.assertThat(readPs1.getUsesSdkLibraries()).isNotNull();
+ Truth.assertThat(readPs1.getUsesSdkLibrariesVersionsMajor()).isNotNull();
+ Truth.assertThat(readPs2).isNotNull();
+ Truth.assertThat(readPs2.getUsesSdkLibraries()).isNotNull();
+ Truth.assertThat(readPs2.getUsesSdkLibrariesVersionsMajor()).isNotNull();
+
+ List<Long> ps1VersionsAsList = new ArrayList<>();
+ for (long version : ps1.getUsesSdkLibrariesVersionsMajor()) {
+ ps1VersionsAsList.add(version);
+ }
+
+ List<Long> ps2VersionsAsList = new ArrayList<>();
+ for (long version : ps2.getUsesSdkLibrariesVersionsMajor()) {
+ ps2VersionsAsList.add(version);
+ }
+
+ Truth.assertThat(readPs1.getUsesSdkLibraries()).asList()
+ .containsExactlyElementsIn(ps1.getUsesSdkLibraries()).inOrder();
+
+ Truth.assertThat(readPs1.getUsesSdkLibrariesVersionsMajor()).asList()
+ .containsExactlyElementsIn(ps1VersionsAsList).inOrder();
+
+ Truth.assertThat(readPs2.getUsesSdkLibraries()).asList()
+ .containsExactlyElementsIn(ps2.getUsesSdkLibraries()).inOrder();
+
+ Truth.assertThat(readPs2.getUsesSdkLibrariesVersionsMajor()).asList()
+ .containsExactlyElementsIn(ps2VersionsAsList).inOrder();
+ }
+
+ @Test
public void testPackageRestrictionsDistractionFlagsDefault() {
final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
assertThat(defaultSetting.getDistractionFlags(0), is(PackageManager.RESTRICTION_NONE));
@@ -569,6 +636,8 @@
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
0,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -591,6 +660,8 @@
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
0,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -607,6 +678,8 @@
0 /*pkgFlags*/,
0 /*pkgPrivateFlags*/,
0,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -635,6 +708,8 @@
0 /*pkgFlags*/,
0 /*pkgPrivateFlags*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -669,6 +744,8 @@
ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -706,6 +783,8 @@
0 /*pkgFlags*/,
0 /*pkgPrivateFlags*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -739,6 +818,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -778,6 +859,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -820,6 +903,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -862,6 +947,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -969,6 +1056,25 @@
assertThat(countDownLatch.getCount(), is(1L));
}
+ @Test
+ public void testSetPkgStateLibraryFiles_addNewSdks() {
+ final PackageSetting packageSetting = createPackageSetting("com.foo");
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ packageSetting.registerObserver(new Watcher() {
+ @Override
+ public void onChange(Watchable what) {
+ countDownLatch.countDown();
+ }
+ });
+
+ final List<String> files = new ArrayList<>();
+ files.add("com.sdk1_123");
+ files.add("com.sdk9_876");
+ packageSetting.setUsesSdkLibraries(files.toArray(new String[files.size()]));
+
+ assertThat(countDownLatch.getCount(), is(0L));
+ }
+
private <T> void assertArrayEquals(T[] a, T[] b) {
assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
Arrays.equals(a, b));
@@ -1004,6 +1110,13 @@
}
}
+ private void verifyKeySetData(PackageKeySetData originalData, PackageKeySetData testData) {
+ assertThat(originalData.getProperSigningKeySet(),
+ equalTo(testData.getProperSigningKeySet()));
+ assertThat(originalData.getUpgradeKeySets(), is(testData.getUpgradeKeySets()));
+ assertThat(originalData.getAliases(), is(testData.getAliases()));
+ }
+
private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
assertThat(origPkgSetting, is(not(testPkgSetting)));
assertThat(origPkgSetting.getAppId(), is(testPkgSetting.getAppId()));
@@ -1018,8 +1131,7 @@
assertSame(origPkgSetting.getInstallSource(), testPkgSetting.getInstallSource());
assertThat(origPkgSetting.isInstallPermissionsFixed(),
is(testPkgSetting.isInstallPermissionsFixed()));
- assertSame(origPkgSetting.getKeySetData(), testPkgSetting.getKeySetData());
- assertThat(origPkgSetting.getKeySetData(), is(testPkgSetting.getKeySetData()));
+ verifyKeySetData(origPkgSetting.getKeySetData(), testPkgSetting.getKeySetData());
assertThat(origPkgSetting.getLastUpdateTime(), is(testPkgSetting.getLastUpdateTime()));
assertSame(origPkgSetting.getLegacyNativeLibraryPath(),
testPkgSetting.getLegacyNativeLibraryPath());
@@ -1082,6 +1194,8 @@
pkgFlags,
0 /*privateFlags*/,
sharedUserId,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -1101,6 +1215,8 @@
0,
0 /*privateFlags*/,
0,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index fb092d2..11bac45 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -932,11 +932,12 @@
.addUsesPermission(new ParsedUsesPermissionImpl("foo7", 0))
.addImplicitPermission("foo25")
.addProtectedBroadcast("foo8")
+ .setSdkLibName("sdk12")
+ .setSdkLibVersionMajor(42)
+ .addUsesSdkLibrary("sdk23", 200, new String[]{"digest2"})
.setStaticSharedLibName("foo23")
.setStaticSharedLibVersion(100)
- .addUsesStaticLibrary("foo23")
- .addUsesStaticLibraryCertDigests(new String[]{"digest"})
- .addUsesStaticLibraryVersion(100)
+ .addUsesStaticLibrary("foo23", 100, new String[]{"digest"})
.addLibraryName("foo10")
.addUsesLibrary("foo11")
.addUsesOptionalLibrary("foo12")
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 2146070..94d8358 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -44,8 +44,6 @@
private SparseArray<PackageUserStateImpl> mUserStates = new SparseArray<>();
private AndroidPackage mPkg;
private InstallSource mInstallSource;
- private String[] mUsesStaticLibraries;
- private long[] mUsesStaticLibrariesVersions;
private Map<String, Set<String>> mMimeGroups;
private SigningDetails mSigningDetails;
private UUID mDomainSetId = UUID.randomUUID();
@@ -116,17 +114,6 @@
return this;
}
- public PackageSettingBuilder setUsesStaticLibraries(String[] usesStaticLibraries) {
- this.mUsesStaticLibraries = usesStaticLibraries;
- return this;
- }
-
- public PackageSettingBuilder setUsesStaticLibrariesVersions(
- long[] usesStaticLibrariesVersions) {
- this.mUsesStaticLibrariesVersions = usesStaticLibrariesVersions;
- return this;
- }
-
public PackageSettingBuilder setMimeGroups(Map<String, Set<String>> mimeGroups) {
this.mMimeGroups = mimeGroups;
return this;
@@ -173,8 +160,9 @@
final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString,
mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
- mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions,
- mMimeGroups, mDomainSetId);
+ mPrivateFlags, mSharedUserId, null /* usesSdkLibraries */,
+ null /* usesSdkLibrariesVersions */, null /* usesStaticLibraries */,
+ null /* usesStaticLibrariesVersions */, mMimeGroups, mDomainSetId);
packageSetting.setSignatures(mSigningDetails != null
? new PackageSignatures(mSigningDetails)
: new PackageSignatures());
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index b6d4b31..7e4474f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
+import android.platform.test.annotations.Presubmit;
import android.util.TypedXmlPullParser;
import android.util.Xml;
@@ -44,6 +45,7 @@
import java.util.Map;
import java.util.Set;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class PackageSignaturesTest {
private static final String TEST_RESOURCES_FOLDER = "PackageSignaturesTest";
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index c9f3cb2..828d419 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -28,6 +28,7 @@
import android.content.pm.SuspendDialogInfo;
import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
+import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -41,6 +42,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageUserStateTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
index 1fff4f0..ecf7803 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -18,8 +18,10 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
+@Presubmit
public class PackageVerificationStateTest extends AndroidTestCase {
private static final int REQUIRED_UID = 1948;
diff --git a/services/tests/servicestests/src/com/android/server/pm/RestrictionsSetTest.java b/services/tests/servicestests/src/com/android/server/pm/RestrictionsSetTest.java
index b73c9ea..e7adf7b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/RestrictionsSetTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/RestrictionsSetTest.java
@@ -28,6 +28,7 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
@@ -37,6 +38,7 @@
import java.util.List;
/** Test for {@link RestrictionsSet}. */
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class RestrictionsSetTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index cfdbb5b7..71d5b77 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.content.pm.SharedLibraryInfo.TYPE_DYNAMIC;
+import static android.content.pm.SharedLibraryInfo.TYPE_SDK;
import static android.content.pm.SharedLibraryInfo.TYPE_STATIC;
import static android.content.pm.SharedLibraryInfo.VERSION_UNDEFINED;
@@ -238,6 +239,37 @@
}
@Test
+ public void installSdkLibrary() throws Exception {
+ final ParsedPackage pkg = ((ParsedPackage) createBasicPackage("ogl.sdk_123")
+ .setSdkLibName("ogl.sdk")
+ .setSdkLibVersionMajor(123)
+ .hideAsParsed())
+ .setPackageName("ogl.sdk_123")
+ .setVersionCodeMajor(5)
+ .setVersionCode(678)
+ .setBaseApkPath("/some/path.apk")
+ .setSplitCodePaths(new String[] {"/some/other/path.apk"});
+
+ final ScanRequest scanRequest = new ScanRequestBuilder(pkg)
+ .setUser(UserHandle.of(0)).build();
+
+ final ScanResult scanResult = executeScan(scanRequest);
+
+ assertThat(scanResult.mSdkSharedLibraryInfo.getPackageName(), is("ogl.sdk_123"));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getName(), is("ogl.sdk"));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getLongVersion(), is(123L));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getType(), is(TYPE_SDK));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getDeclaringPackage().getPackageName(),
+ is("ogl.sdk_123"));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(),
+ is(pkg.getLongVersionCode()));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getAllCodePaths(),
+ hasItems("/some/path.apk", "/some/other/path.apk"));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getDependencies(), nullValue());
+ assertThat(scanResult.mSdkSharedLibraryInfo.getDependentPackages(), empty());
+ }
+
+ @Test
public void installStaticSharedLibrary() throws Exception {
final ParsedPackage pkg = ((ParsedPackage) createBasicPackage("static.lib.pkg")
.setStaticSharedLibName("static.lib")
@@ -528,10 +560,10 @@
"/data/tmp/randompath/base.apk", createCodePath(packageName),
mock(TypedArray.class), false)
.setVolumeUuid(UUID_ONE.toString())
- .addUsesStaticLibrary("some.static.library")
- .addUsesStaticLibraryVersion(234L)
- .addUsesStaticLibrary("some.other.static.library")
- .addUsesStaticLibraryVersion(456L)
+ .addUsesStaticLibrary("some.static.library", 234L, new String[]{"testCert1"})
+ .addUsesStaticLibrary("some.other.static.library", 456L, new String[]{"testCert2"})
+ .addUsesSdkLibrary("some.sdk.library", 123L, new String[]{"testCert3"})
+ .addUsesSdkLibrary("some.other.sdk.library", 789L, new String[]{"testCert4"})
.hideAsParsed())
.setNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib")
.setVersionCodeMajor(1)
@@ -557,6 +589,9 @@
assertThat(pkgSetting.getUsesStaticLibraries(),
arrayContaining("some.static.library", "some.other.static.library"));
assertThat(pkgSetting.getUsesStaticLibrariesVersions(), is(new long[]{234L, 456L}));
+ assertThat(pkgSetting.getUsesSdkLibraries(),
+ arrayContaining("some.sdk.library", "some.other.sdk.library"));
+ assertThat(pkgSetting.getUsesSdkLibrariesVersionsMajor(), is(new long[]{123L, 789L}));
assertThat(pkgSetting.getPkg(), is(scanResult.mRequest.mParsedPackage));
assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
assertThat(pkgSetting.getVersionCode(),
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index ec5228f..32a88bd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -95,13 +95,15 @@
import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import androidx.test.filters.SmallTest;
+
import com.android.frameworks.servicestests.R;
import com.android.server.pm.ShortcutService.ConfigConstants;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
@@ -135,6 +137,7 @@
adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest1 \
-w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
index e92c849..57ada9b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
@@ -15,10 +15,13 @@
*/
package com.android.server.pm;
-import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
- .assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
@@ -26,9 +29,9 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.os.Process;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
-import static org.mockito.Mockito.*;
+import androidx.test.filters.SmallTest;
/**
* Tests for {@link ShortcutManager#createShortcutResultIntent(ShortcutInfo)} and relevant APIs.
@@ -39,6 +42,7 @@
adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest10 \
-w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest10 extends BaseShortcutManagerTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
index c8a4052..98fa2d6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
@@ -30,6 +30,7 @@
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.ShortcutInfo;
import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
import com.android.server.pm.ShortcutService.ConfigConstants;
@@ -42,6 +43,7 @@
*
atest -c com.android.server.pm.ShortcutManagerTest11
*/
+@Presubmit
public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
private static final ShortcutQuery QUERY_MATCH_ALL = createShortcutQuery(
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index bc2d256..bcd216d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -49,8 +49,11 @@
@Override
protected void tearDown() throws Exception {
- setCaller(CALLING_PACKAGE_1, USER_0);
- mService.getPackageShortcutForTest(CALLING_PACKAGE_1, USER_0).removeAllShortcutsAsync();
+ if (mService.isAppSearchEnabled()) {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mService.getPackageShortcutForTest(CALLING_PACKAGE_1, USER_0)
+ .removeAllShortcutsAsync();
+ }
super.tearDown();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 90a1277..408d2c5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -43,8 +43,10 @@
import android.net.Uri;
import android.os.PersistableBundle;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
import com.android.frameworks.servicestests.R;
import com.android.server.pm.ShortcutUser.PackageWithUser;
@@ -64,6 +66,7 @@
adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest2 \
-w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
// ShortcutInfo tests
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
index ba26f79..43e527c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
@@ -21,7 +21,9 @@
import android.content.ComponentName;
import android.content.pm.ShortcutInfo;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
import com.android.frameworks.servicestests.R;
import com.android.server.pm.ShortcutService.ConfigConstants;
@@ -31,6 +33,7 @@
/**
* Tests related to shortcut rank auto-adjustment.
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest3 extends BaseShortcutManagerTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java
index 7546c43..11a2a8a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java
@@ -24,15 +24,17 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
import android.util.Xml;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ShortcutManagerTest4 extends BaseShortcutManagerTest {
@@ -134,4 +136,4 @@
});
});
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
index 203b2ca..400d3a8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
@@ -25,7 +25,9 @@
import android.content.pm.ShortcutServiceInternal;
import android.content.res.XmlResourceParser;
import android.os.Looper;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
import com.android.server.LocalServices;
@@ -38,6 +40,7 @@
* All the tests here actually talks to the real IPackageManager, so we can't test complicated
* cases. Instead we just make sure they all work reasonably without at least crashing.
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest5 extends BaseShortcutManagerTest {
private ShortcutService mShortcutService;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
index 63df4bc..6c10bfd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
@@ -15,12 +15,15 @@
*/
package com.android.server.pm;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
/**
* Tests for {@link ShortcutService#hasShortcutHostPermissionInner}, which includes
* {@link ShortcutService#getDefaultLauncher}.
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest6 extends BaseShortcutManagerTest {
public void testHasShortcutHostPermissionInner_with3pLauncher_complicated() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index b21b049..b2fd8aa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -33,7 +33,9 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
import com.android.frameworks.servicestests.R;
import com.android.server.pm.ShortcutService.ConfigConstants;
@@ -48,6 +50,7 @@
*
* Launcher related commands are tested in
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index 58e00f2..2293808 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -40,11 +40,13 @@
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+
import com.android.frameworks.servicestests.R;
import org.mockito.ArgumentCaptor;
@@ -63,6 +65,7 @@
* - Reading icons from requested shortcuts.
* - Invalid pre-approved token.
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
private ShortcutRequestPinProcessor mProcessor;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
index 55b4b93..a47a8df 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
@@ -32,7 +32,9 @@
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.PinItemRequest;
import android.os.UserHandle;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
import org.mockito.ArgumentCaptor;
@@ -46,6 +48,7 @@
adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest9 \
-w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
*/
+@Presubmit
@SmallTest
public class ShortcutManagerTest9 extends BaseShortcutManagerTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
index 826a8d4..4af91c6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertNull;
import android.content.pm.SuspendDialogInfo;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -31,6 +32,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SuspendDialogInfoTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING
new file mode 100644
index 0000000..85a73bb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING
@@ -0,0 +1,41 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Postsubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
index 7916bd3..a4afe09 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
@@ -24,6 +24,7 @@
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserManager;
+import android.platform.test.annotations.Postsubmit;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -42,6 +43,7 @@
* To run the test:
* bit FrameworksServicesTests:com.android.server.pm.UserLifecycleStressTest
*/
+@Postsubmit
@RunWith(AndroidJUnit4.class)
@LargeTest
public class UserLifecycleStressTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index 35c513f..fdf94be 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -27,6 +27,7 @@
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.Postsubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -48,6 +49,7 @@
* runtest -c com.android.server.pm.UserManagerServiceCreateProfileTest frameworks-services
* </pre>
*/
+@Postsubmit
@RunWith(AndroidJUnit4.class)
@MediumTest
public class UserManagerServiceCreateProfileTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
index b0423bf..1f4c9f8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
@@ -23,6 +23,7 @@
import android.app.PropertyInvalidatedCache;
import android.content.pm.UserInfo;
import android.os.Looper;
+import android.platform.test.annotations.Postsubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -45,6 +46,7 @@
* -w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
* </pre>
*/
+@Postsubmit
@RunWith(AndroidJUnit4.class)
@MediumTest
public class UserManagerServiceIdRecyclingTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 6c1c019..34b40c7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -22,18 +22,20 @@
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.Postsubmit;
import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
import android.util.AtomicFile;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
+@Postsubmit
@SmallTest
public class UserManagerServiceTest extends AndroidTestCase {
private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index dfc25e0..92fddc7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -46,6 +46,7 @@
import android.os.Parcel;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
@@ -69,6 +70,7 @@
* runtest -c com.android.server.pm.UserManagerServiceUserInfoTest frameworks-services
* </pre>
*/
+@Presubmit
@RunWith(AndroidJUnit4.class)
@MediumTest
public class UserManagerServiceUserInfoTest {
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 f1acc66..971b036 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -39,6 +39,7 @@
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import androidx.test.InstrumentationRegistry;
@@ -58,6 +59,7 @@
*
* <p>Run with: atest UserManagerServiceUserTypeTest
*/
+@Presubmit
@RunWith(AndroidJUnit4.class)
@MediumTest
public class UserManagerServiceUserTypeTest {
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 b76c279..cf6165f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -35,14 +35,15 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.Postsubmit;
import android.provider.Settings;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Slog;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.google.common.collect.Range;
@@ -63,6 +64,7 @@
import javax.annotation.concurrent.GuardedBy;
/** Test {@link UserManager} functionality. */
+@Postsubmit
@RunWith(AndroidJUnit4.class)
public final class UserManagerTest {
// Taken from UserManagerService
@@ -207,6 +209,65 @@
assertThat(hasUser(user2.id)).isTrue();
}
+ /**
+ * Tests that UserManager knows how many users can be created.
+ *
+ * We can only test this with regular secondary users, since some other user types have weird
+ * rules about when or if they count towards the max.
+ */
+ @MediumTest
+ @Test
+ public void testAddTooManyUsers() throws Exception {
+ final String userType = UserManager.USER_TYPE_FULL_SECONDARY;
+ final UserTypeDetails userTypeDetails = UserTypeFactory.getUserTypes().get(userType);
+
+ final int maxUsersForType = userTypeDetails.getMaxAllowed();
+ final int maxUsersOverall = UserManager.getMaxSupportedUsers();
+
+ int currentUsersOfType = 0;
+ int currentUsersOverall = 0;
+ final List<UserInfo> userList = mUserManager.getAliveUsers();
+ for (UserInfo user : userList) {
+ currentUsersOverall++;
+ if (userType.equals(user.userType)) {
+ currentUsersOfType++;
+ }
+ }
+
+ final int remainingUserType = maxUsersForType == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS ?
+ Integer.MAX_VALUE : maxUsersForType - currentUsersOfType;
+ final int remainingOverall = maxUsersOverall - currentUsersOverall;
+ final int remaining = Math.min(remainingUserType, remainingOverall);
+
+ Slog.v(TAG, "maxUsersForType=" + maxUsersForType
+ + ", maxUsersOverall=" + maxUsersOverall
+ + ", currentUsersOfType=" + currentUsersOfType
+ + ", currentUsersOverall=" + currentUsersOverall
+ + ", remaining=" + remaining);
+
+ assumeTrue("Device supports too many users for this test to be practical", remaining < 20);
+
+ int usersAdded;
+ for (usersAdded = 0; usersAdded < remaining; usersAdded++) {
+ Slog.v(TAG, "Adding user " + usersAdded);
+ assertThat(mUserManager.canAddMoreUsers()).isTrue();
+ assertThat(mUserManager.canAddMoreUsers(userType)).isTrue();
+
+ final UserInfo user = createUser("User " + usersAdded, userType, 0);
+ assertThat(user).isNotNull();
+ assertThat(hasUser(user.id)).isTrue();
+ }
+ Slog.v(TAG, "Added " + usersAdded + " users.");
+
+ assertWithMessage("Still thinks more users of that type can be added")
+ .that(mUserManager.canAddMoreUsers(userType)).isFalse();
+ if (currentUsersOverall + usersAdded >= maxUsersOverall) {
+ assertThat(mUserManager.canAddMoreUsers()).isFalse();
+ }
+
+ assertThat(createUser("User beyond", userType, 0)).isNull();
+ }
+
@MediumTest
@Test
public void testRemoveUser() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index ddf0cd0..07a5303 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -22,10 +22,12 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.SparseArray;
+import androidx.test.filters.SmallTest;
+
/**
* Tests for {@link com.android.server.pm.UserRestrictionsUtils}.
*
@@ -37,6 +39,7 @@
-w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
* </pre>
*/
+@Presubmit
@SmallTest
public class UserRestrictionsUtilsTest extends AndroidTestCase {
public void testNonNull() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index b11bb85..ba7a103 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -43,6 +43,7 @@
import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserManager;
+import android.platform.test.annotations.Postsubmit;
import android.support.test.uiautomator.UiDevice;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -76,6 +77,7 @@
* atest com.android.server.pm.UserSystemPackageInstallerTest
* </pre>
*/
+@Postsubmit
@RunWith(AndroidJUnit4.class)
@MediumTest
public class UserSystemPackageInstallerTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
index b2c3002..95af1e1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.IntentFilter;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -29,6 +30,7 @@
import java.util.Iterator;
+@Presubmit
@SmallTest
public class WatchedIntentHandlingTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
index fdb6e9f..a16ecb1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -18,6 +18,8 @@
import static org.mockito.Mockito.inOrder;
+import android.platform.test.annotations.Presubmit;
+
import com.android.internal.art.ArtStatsLog;
import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
@@ -44,6 +46,7 @@
*
* Run with "atest ArtStatsLogUtilsTest".
*/
+@Presubmit
@RunWith(JUnit4.class)
public final class ArtStatsLogUtilsTest {
private static final String TAG = ArtStatsLogUtilsTest.class.getSimpleName();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index 2a7a2ff..b7b55ba 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -30,6 +30,7 @@
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -61,6 +62,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DexMetadataHelperTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index bc84e35..d5893c8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -23,6 +23,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.Presubmit;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -32,6 +34,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DexoptOptionsTests {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 34cefec..1dcb0b7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -24,6 +24,7 @@
import android.content.pm.SharedLibraryInfo;
import android.content.pm.parsing.ParsingPackage;
+import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
@@ -46,6 +47,7 @@
import java.util.Collections;
import java.util.List;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DexoptUtilsTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
index 7992ba3..d55f967 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageInfo;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -51,6 +52,7 @@
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Stubber;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DynamicCodeLoggerTests {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 3450710..c98e7c3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.fail;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -49,6 +50,7 @@
import java.util.Map;
import java.util.Set;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageDexUsageTests {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
index f4cdc8c..e075379 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
@@ -30,6 +30,8 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import android.platform.test.annotations.Presubmit;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -50,6 +52,7 @@
import java.util.Objects;
import java.util.Set;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageDynamicCodeLoadingTests {
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
index 51c268e..4059a49 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
@@ -16,17 +16,18 @@
package com.android.server.pm.parsing
+import android.Manifest
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageParser
import android.platform.test.annotations.Postsubmit
+import com.android.internal.util.ArrayUtils
import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.appInfo
import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.pkgInfo
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -91,9 +92,18 @@
listOf(it.configPreferences, it.reqFeatures, it.featureGroups)
},
pkgInfo(PackageManager.GET_PERMISSIONS) {
- listOf(it.permissions, it.requestedPermissions, it.requestedPermissionsFlags)
+ listOf(
+ it.permissions,
+ // Strip compatibility permission added in T
+ it.requestedPermissions?.filter { x ->
+ x != Manifest.permission.POST_NOTIFICATIONS
+ }?.ifEmpty { null }?.toTypedArray(),
+ // Strip the flag from compatibility permission added in T
+ it.requestedPermissionsFlags?.filterIndexed { index, _ ->
+ index != ArrayUtils.indexOf(it.requestedPermissions,
+ Manifest.permission.POST_NOTIFICATIONS)
+ }?.ifEmpty { null }?.toTypedArray())
},
-
appInfo(PackageManager.GET_META_DATA) { listOf(it.metaData) },
appInfo(PackageManager.GET_SHARED_LIBRARY_FILES) {
listOf(it.sharedLibraryFiles, it.sharedLibraryFiles)
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index df8786f..122661e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -16,6 +16,7 @@
package com.android.server.pm.parsing
+import android.Manifest
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
@@ -34,6 +35,7 @@
import android.os.Process
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.util.ArrayUtils
import com.android.server.pm.PackageManagerService
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageStateInternal
@@ -145,8 +147,8 @@
flags: Int = 0,
userId: Int = 0
): ApplicationInfo? {
- return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, userId,
- mockPkgSetting(pkg))
+ return PackageInfoUtils.generateApplicationInfo(pkg, flags.toLong(), dummyUserState,
+ userId, mockPkgSetting(pkg))
}
fun newAppInfoWithoutState(
@@ -154,8 +156,8 @@
flags: Int = 0,
userId: Int = 0
): ApplicationInfo? {
- return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, userId,
- mockPkgSetting(pkg))
+ return PackageInfoUtils.generateApplicationInfo(pkg, flags.toLong(), dummyUserState,
+ userId, mockPkgSetting(pkg))
}
fun oldPackageInfo(pkg: PackageParser.Package, flags: Int = 0): PackageInfo? {
@@ -164,7 +166,7 @@
}
fun newPackageInfo(pkg: AndroidPackage, flags: Int = 0): PackageInfo? {
- return PackageInfoUtils.generate(pkg, intArrayOf(), flags, 5, 6, emptySet(),
+ return PackageInfoUtils.generate(pkg, intArrayOf(), flags.toLong(), 5, 6, emptySet(),
dummyUserState, 0, mockPkgSetting(pkg))
}
@@ -329,7 +331,10 @@
.ignored("Update for fixing b/128526493 and the testing is no longer valid")}
enabled=${this.enabled}
exported=${this.exported}
- flags=${Integer.toBinaryString(this.flags)}
+ flags=${Integer.toBinaryString(
+ // Strip flag added in T
+ this.flags and (ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES.inv()))
+ }
icon=${this.icon}
labelRes=${this.labelRes}
launchMode=${this.launchMode}
@@ -501,13 +506,22 @@
receivers=${this.receivers?.joinToString { it.dumpToString() }
.ignored("Checked separately in test")}
reqFeatures=${this.reqFeatures?.joinToString { it.dumpToString() }}
- requestedPermissions=${this.requestedPermissions?.contentToString()}
+ requestedPermissions=${
+ // Strip compatibility permission added in T
+ this.requestedPermissions?.filter { x ->
+ x != Manifest.permission.POST_NOTIFICATIONS
+ }?.ifEmpty { null }?.joinToString()
+ }
requestedPermissionsFlags=${
- this.requestedPermissionsFlags?.map {
+ // Strip the flag from compatibility permission added in T
+ this.requestedPermissionsFlags?.filterIndexed { index, _ ->
+ index != ArrayUtils.indexOf(requestedPermissions,
+ Manifest.permission.POST_NOTIFICATIONS)
+ }?.map {
// Newer flags are stripped
it and (PackageInfo.REQUESTED_PERMISSION_REQUIRED
or PackageInfo.REQUESTED_PERMISSION_GRANTED)
- }?.joinToString()
+ }?.ifEmpty { null }?.joinToString()
}
requiredAccountType=${this.requiredAccountType}
requiredForAllUsers=${this.requiredForAllUsers}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index c4aa862..f530421 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.parsing.ParsingPackage
import android.content.pm.parsing.ParsingPackageUtils
import android.content.pm.parsing.result.ParseResult
+import android.platform.test.annotations.Presubmit
import androidx.test.InstrumentationRegistry
import com.android.frameworks.servicestests.R
import com.google.common.truth.Truth.assertThat
@@ -36,6 +37,7 @@
*
* This verifies these failures when the APK targets R.
*/
+@Presubmit
class PackageParsingDeferErrorTest {
companion object {
diff --git a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
index 3261dfa..3551af8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Process;
+import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -41,6 +42,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class LegacyPermissionManagerServiceTest {
private static final int SYSTEM_UID = 1000;
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 117680b..6ee6020c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -48,13 +48,14 @@
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
- .setAutoDetectionEnabled(true)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(true)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
.build();
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabled(true)
+ .setAutoDetectionEnabledSetting(true)
.build();
assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
@@ -79,7 +80,7 @@
{
ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabled(false)
+ .setAutoDetectionEnabledSetting(false)
.build();
assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
@@ -110,13 +111,14 @@
.setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
- .setAutoDetectionEnabled(true)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(true)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
.build();
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabled(true)
+ .setAutoDetectionEnabledSetting(true)
.build();
assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
@@ -142,7 +144,7 @@
{
ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabled(false)
+ .setAutoDetectionEnabledSetting(false)
.build();
assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
@@ -174,13 +176,14 @@
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
- .setAutoDetectionEnabled(true)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(true)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
.build();
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabled(true)
+ .setAutoDetectionEnabledSetting(true)
.build();
assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
@@ -203,7 +206,7 @@
}
{
ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabled(false)
+ .setAutoDetectionEnabledSetting(false)
.build();
assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
@@ -236,13 +239,14 @@
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(false)
- .setAutoDetectionEnabled(true)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(true)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
.build();
{
ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabled(true)
+ .setAutoDetectionEnabledSetting(true)
.build();
assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
@@ -266,7 +270,7 @@
}
{
ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
- .setAutoDetectionEnabled(false)
+ .setAutoDetectionEnabledSetting(false)
.build();
assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
@@ -288,4 +292,18 @@
assertTrue(configuration.isGeoDetectionEnabled());
}
}
+
+ @Test
+ public void test_telephonyFallbackSupported() {
+ ConfigurationInternal config = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+ .setUserConfigAllowed(true)
+ .setTelephonyDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(false)
+ .setTelephonyFallbackSupported(true)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
+ .build();
+ assertTrue(config.isTelephonyFallbackSupported());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
index 9d1c74b..a97ad8c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
@@ -151,12 +151,12 @@
}
@Override
- public void setRecordProviderStateChanges(boolean enabled) {
+ public void setRecordStateChangesForTests(boolean enabled) {
failUnimplemented();
}
@Override
- public boolean getRecordProviderStateChanges() {
+ public boolean getRecordStateChangesForTests() {
return failUnimplemented();
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index ee3195e..2d0dca2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -51,6 +51,11 @@
}
@Override
+ public void enableTelephonyTimeZoneFallback() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public MetricsTimeZoneDetectorState generateMetricsState() {
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
index 97b8360..97095c4 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* A test support class used for tracking a piece of state in test objects like fakes and mocks.
@@ -79,6 +80,11 @@
assertEquals(expectedCount, getChangeCount());
}
+ /** Asserts the value has been {@link #set} to the expected values in the order given. */
+ public void assertChanges(T... expected) {
+ assertEquals(Arrays.asList(expected), mValues);
+ }
+
/**
* Returns the latest value passed to {@link #set}. If {@link #set} hasn't been called then the
* initial value is returned.
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index ac2b27f..193b2e3 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -190,6 +190,9 @@
createTimeZoneConfiguration(true /* autoDetectionEnabled */);
mTimeZoneDetectorService.updateConfiguration(autoDetectEnabledConfiguration);
+ // The configuration update notification is asynchronous.
+ mTestHandler.waitForMessagesToBeProcessed();
+
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
anyString());
@@ -369,16 +372,17 @@
}
private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) {
- // Default geo detection settings from auto detection settings - they are not important to
- // the tests.
+ // Default geo detection settings from the auto detection setting - they are not important
+ // to the tests.
final boolean geoDetectionEnabled = autoDetectionEnabled;
return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
+ .setTelephonyFallbackSupported(false)
.setUserConfigAllowed(true)
- .setAutoDetectionEnabled(autoDetectionEnabled)
- .setLocationEnabled(geoDetectionEnabled)
- .setGeoDetectionEnabled(geoDetectionEnabled)
+ .setAutoDetectionEnabledSetting(autoDetectionEnabled)
+ .setLocationEnabledSetting(geoDetectionEnabled)
+ .setGeoDetectionEnabledSetting(geoDetectionEnabled)
.build();
}
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 e6036c4..ef1b4f5 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -60,6 +60,7 @@
public class TimeZoneDetectorStrategyImplTest {
private static final @UserIdInt int USER_ID = 9876;
+ private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
/** A time zone used for initialization that does not occur elsewhere in tests. */
private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
private static final int SLOT_INDEX1 = 10000;
@@ -89,9 +90,10 @@
.setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
- .setAutoDetectionEnabled(false)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(false)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(false)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(false)
.build();
private static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_ENABLED =
@@ -99,9 +101,10 @@
.setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
- .setAutoDetectionEnabled(true)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(true)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
.build();
private static final ConfigurationInternal CONFIG_AUTO_DETECT_NOT_SUPPORTED =
@@ -109,9 +112,10 @@
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
- .setAutoDetectionEnabled(false)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(false)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(false)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(false)
.build();
private static final ConfigurationInternal CONFIG_AUTO_DISABLED_GEO_DISABLED =
@@ -119,37 +123,37 @@
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
- .setAutoDetectionEnabled(false)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(false)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(false)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(false)
.build();
private static final ConfigurationInternal CONFIG_AUTO_ENABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
+ .setTelephonyFallbackSupported(false)
.setUserConfigAllowed(true)
- .setAutoDetectionEnabled(true)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(false)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(false)
.build();
private static final ConfigurationInternal CONFIG_AUTO_ENABLED_GEO_ENABLED =
new ConfigurationInternal.Builder(USER_ID)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
+ .setTelephonyFallbackSupported(false)
.setUserConfigAllowed(true)
- .setAutoDetectionEnabled(true)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(true)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
.build();
private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
private FakeEnvironment mFakeEnvironment;
- // A fake source of time for suggestions. This will typically be incremented after every use.
- @ElapsedRealtimeLong private long mElapsedRealtimeMillis;
-
@Before
public void setUp() {
mFakeEnvironment = new FakeEnvironment();
@@ -531,7 +535,7 @@
boolean geoDetectionEnabled) {
ConfigurationInternal geoTzEnabledConfig =
new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED_GEO_DISABLED)
- .setGeoDetectionEnabled(geoDetectionEnabled)
+ .setGeoDetectionEnabledSetting(geoDetectionEnabled)
.build();
Script script = new Script()
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
@@ -753,6 +757,204 @@
}
@Test
+ public void testTelephonyFallback() {
+ ConfigurationInternal config = new ConfigurationInternal.Builder(
+ CONFIG_AUTO_ENABLED_GEO_ENABLED)
+ .setTelephonyFallbackSupported(true)
+ .build();
+
+ Script script = new Script()
+ .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+ .simulateConfigurationInternalChange(config)
+ .resetConfigurationTracking();
+
+ // Confirm initial state is as expected.
+ script.verifyTelephonyFallbackIsEnabled(true)
+ .verifyTimeZoneNotChanged();
+
+ // Although geolocation detection is enabled, telephony fallback should be used initially
+ // and until a suitable "certain" geolocation suggestion is received.
+ {
+ TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion(
+ SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
+ "Europe/Paris");
+ script.simulateIncrementClock()
+ .simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
+ .verifyTimeZoneChangedAndReset(telephonySuggestion)
+ .verifyTelephonyFallbackIsEnabled(true);
+ }
+
+ // Receiving an "uncertain" geolocation suggestion should have no effect.
+ {
+ GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
+ createUncertainGeolocationSuggestion();
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(true);
+ }
+
+ // Receiving a "certain" geolocation suggestion should disable telephony fallback mode.
+ {
+ GeolocationTimeZoneSuggestion geolocationSuggestion =
+ createCertainGeolocationSuggestion("Europe/London");
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
+ .verifyTimeZoneChangedAndReset(geolocationSuggestion)
+ .verifyTelephonyFallbackIsEnabled(false);
+ }
+
+ // Used to record the last telephony suggestion received, which will be used when fallback
+ // takes place.
+ TelephonyTimeZoneSuggestion lastTelephonySuggestion;
+
+ // Telephony suggestions should now be ignored and geolocation detection is "in control".
+ {
+ TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion(
+ SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
+ "Europe/Berlin");
+ script.simulateIncrementClock()
+ .simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(false);
+ lastTelephonySuggestion = telephonySuggestion;
+ }
+
+ // Geolocation suggestions should continue to be used as normal (previous telephony
+ // suggestions are not used, even when the geolocation suggestion is uncertain).
+ {
+ GeolocationTimeZoneSuggestion geolocationSuggestion =
+ createCertainGeolocationSuggestion("Europe/Rome");
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
+ .verifyTimeZoneChangedAndReset(geolocationSuggestion)
+ .verifyTelephonyFallbackIsEnabled(false);
+
+ GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
+ createUncertainGeolocationSuggestion();
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(false);
+
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
+ // No change needed, device will already be set to Europe/Rome.
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(false);
+ }
+
+ // Enable telephony fallback. Nothing will change, because the geolocation is still certain,
+ // but fallback will remain enabled.
+ {
+ script.simulateIncrementClock()
+ .simulateEnableTelephonyFallback()
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(true);
+ }
+
+ // Make the geolocation algorithm uncertain.
+ {
+ GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
+ createUncertainGeolocationSuggestion();
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .verifyTimeZoneChangedAndReset(lastTelephonySuggestion)
+ .verifyTelephonyFallbackIsEnabled(true);
+ }
+
+ // Make the geolocation algorithm certain, disabling telephony fallback.
+ {
+ GeolocationTimeZoneSuggestion geolocationSuggestion =
+ createCertainGeolocationSuggestion("Europe/Lisbon");
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
+ .verifyTimeZoneChangedAndReset(geolocationSuggestion)
+ .verifyTelephonyFallbackIsEnabled(false);
+
+ }
+
+ // Demonstrate what happens when geolocation is uncertain when telephony fallback is
+ // enabled.
+ {
+ GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
+ createUncertainGeolocationSuggestion();
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(false)
+ .simulateEnableTelephonyFallback()
+ .verifyTimeZoneChangedAndReset(lastTelephonySuggestion)
+ .verifyTelephonyFallbackIsEnabled(true);
+ }
+ }
+
+ @Test
+ public void testTelephonyFallback_noTelephonySuggestionToFallBackTo() {
+ ConfigurationInternal config = new ConfigurationInternal.Builder(
+ CONFIG_AUTO_ENABLED_GEO_ENABLED)
+ .setTelephonyFallbackSupported(true)
+ .build();
+
+ Script script = new Script()
+ .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+ .simulateConfigurationInternalChange(config)
+ .resetConfigurationTracking();
+
+ // Confirm initial state is as expected.
+ script.verifyTelephonyFallbackIsEnabled(true)
+ .verifyTimeZoneNotChanged();
+
+ // Receiving an "uncertain" geolocation suggestion should have no effect.
+ {
+ GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
+ createUncertainGeolocationSuggestion();
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(true);
+ }
+
+ // Make an uncertain geolocation suggestion, there is no telephony suggestion to fall back
+ // to
+ {
+ GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
+ createUncertainGeolocationSuggestion();
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(true);
+ }
+
+ // Similar to the case above, but force a fallback attempt after making a "certain"
+ // geolocation suggestion.
+ // Geolocation suggestions should continue to be used as normal (previous telephony
+ // suggestions are not used, even when the geolocation suggestion is uncertain).
+ {
+ GeolocationTimeZoneSuggestion geolocationSuggestion =
+ createCertainGeolocationSuggestion("Europe/Rome");
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
+ .verifyTimeZoneChangedAndReset(geolocationSuggestion)
+ .verifyTelephonyFallbackIsEnabled(false);
+
+ GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
+ createUncertainGeolocationSuggestion();
+ script.simulateIncrementClock()
+ .simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(false);
+
+ script.simulateIncrementClock()
+ .simulateEnableTelephonyFallback()
+ .verifyTimeZoneNotChanged()
+ .verifyTelephonyFallbackIsEnabled(true);
+ }
+ }
+
+ @Test
public void testGenerateMetricsState() {
ConfigurationInternal expectedInternalConfig = CONFIG_AUTO_DISABLED_GEO_DISABLED;
String expectedDeviceTimeZoneId = "InitialZoneId";
@@ -792,8 +994,8 @@
// Update the config and confirm that the config metrics state updates also.
expectedInternalConfig = new ConfigurationInternal.Builder(expectedInternalConfig)
- .setAutoDetectionEnabled(true)
- .setGeoDetectionEnabled(true)
+ .setAutoDetectionEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
.build();
expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
@@ -835,6 +1037,8 @@
assertEquals(config.isTelephonyDetectionSupported(),
actualState.isTelephonyDetectionSupported());
assertEquals(config.isGeoDetectionSupported(), actualState.isGeoDetectionSupported());
+ assertEquals(config.isTelephonyFallbackSupported(),
+ actualState.isTelephonyTimeZoneFallbackSupported());
assertEquals(config.getAutoDetectionEnabledSetting(),
actualState.getAutoDetectionEnabledSetting());
assertEquals(config.getGeoDetectionEnabledSetting(),
@@ -865,7 +1069,7 @@
private GeolocationTimeZoneSuggestion createUncertainGeolocationSuggestion() {
return GeolocationTimeZoneSuggestion.createCertainSuggestion(
- mElapsedRealtimeMillis++, null);
+ mFakeEnvironment.elapsedRealtimeMillis(), null);
}
private GeolocationTimeZoneSuggestion createCertainGeolocationSuggestion(
@@ -874,7 +1078,7 @@
GeolocationTimeZoneSuggestion suggestion =
GeolocationTimeZoneSuggestion.createCertainSuggestion(
- mElapsedRealtimeMillis++, Arrays.asList(zoneIds));
+ mFakeEnvironment.elapsedRealtimeMillis(), Arrays.asList(zoneIds));
suggestion.addDebugInfo("Test suggestion");
return suggestion;
}
@@ -883,16 +1087,25 @@
private final TestState<String> mTimeZoneId = new TestState<>();
private ConfigurationInternal mConfigurationInternal;
+ private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
private ConfigurationChangeListener mConfigurationInternalChangeListener;
void initializeConfig(ConfigurationInternal configurationInternal) {
mConfigurationInternal = configurationInternal;
}
+ void initializeClock(@ElapsedRealtimeLong long elapsedRealtimeMillis) {
+ mElapsedRealtimeMillis = elapsedRealtimeMillis;
+ }
+
void initializeTimeZoneSetting(String zoneId) {
mTimeZoneId.init(zoneId);
}
+ void incrementClock() {
+ mElapsedRealtimeMillis++;
+ }
+
@Override
public void setConfigurationInternalChangeListener(ConfigurationChangeListener listener) {
mConfigurationInternalChangeListener = listener;
@@ -936,6 +1149,12 @@
void commitAllChanges() {
mTimeZoneId.commitLatest();
}
+
+ @Override
+ @ElapsedRealtimeLong
+ public long elapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
}
/**
@@ -949,6 +1168,16 @@
return this;
}
+ Script initializeClock(long elapsedRealtimeMillis) {
+ mFakeEnvironment.initializeClock(elapsedRealtimeMillis);
+ return this;
+ }
+
+ Script simulateIncrementClock() {
+ mFakeEnvironment.incrementClock();
+ return this;
+ }
+
/**
* Simulates the user / user's configuration changing.
*/
@@ -963,7 +1192,7 @@
Script simulateSetAutoMode(boolean autoDetectionEnabled) {
ConfigurationInternal newConfig = new ConfigurationInternal.Builder(
mFakeEnvironment.getCurrentUserConfigurationInternal())
- .setAutoDetectionEnabled(autoDetectionEnabled)
+ .setAutoDetectionEnabledSetting(autoDetectionEnabled)
.build();
simulateConfigurationInternalChange(newConfig);
return this;
@@ -975,7 +1204,7 @@
Script simulateSetGeoDetectionEnabled(boolean geoDetectionEnabled) {
ConfigurationInternal newConfig = new ConfigurationInternal.Builder(
mFakeEnvironment.getCurrentUserConfigurationInternal())
- .setGeoDetectionEnabled(geoDetectionEnabled)
+ .setGeoDetectionEnabledSetting(geoDetectionEnabled)
.build();
simulateConfigurationInternalChange(newConfig);
return this;
@@ -1009,6 +1238,15 @@
}
/**
+ * Simulates the time zone detection strategty receiving a signal that allows it to do
+ * telephony fallback.
+ */
+ Script simulateEnableTelephonyFallback() {
+ mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback();
+ return this;
+ }
+
+ /**
* Confirms that the device's time zone has not been set by previous actions since the test
* state was last reset.
*/
@@ -1044,6 +1282,13 @@
return this;
}
+ /** Verifies the state for telephony fallback. */
+ Script verifyTelephonyFallbackIsEnabled(boolean expectedEnabled) {
+ assertEquals(expectedEnabled,
+ mTimeZoneDetectorStrategy.isTelephonyFallbackEnabledForTests());
+ return this;
+ }
+
Script resetConfigurationTracking() {
mFakeEnvironment.commitAllChanges();
return this;
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
similarity index 68%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
index 463ac52..d54e1f1 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
@@ -21,6 +21,14 @@
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_PROVIDERS_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNKNOWN;
import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
import static com.android.server.timezonedetector.location.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED;
@@ -45,7 +53,9 @@
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
import com.android.server.timezonedetector.TestState;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
+import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;
import org.junit.Before;
import org.junit.Test;
@@ -57,9 +67,9 @@
import java.util.List;
import java.util.Objects;
-/** Tests for {@link ControllerImpl}. */
+/** Tests for {@link LocationTimeZoneProviderController}. */
@Presubmit
-public class ControllerImplTest {
+public class LocationTimeZoneProviderControllerTest {
private static final long ARBITRARY_TIME_MILLIS = 12345L;
@@ -73,6 +83,7 @@
TimeZoneProviderEvent.createPermanentFailureEvent(ARBITRARY_TIME_MILLIS, "Test");
private TestThreadingDomain mTestThreadingDomain;
+ private TestMetricsLogger mTestMetricsLogger;
private TestCallback mTestCallback;
private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider;
private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider;
@@ -82,11 +93,12 @@
// For simplicity, the TestThreadingDomain uses the test's main thread. To execute posted
// runnables, the test must call methods on mTestThreadingDomain otherwise those runnables
// will never get a chance to execute.
- LocationTimeZoneProvider.ProviderMetricsLogger stubbedProviderMetricsLogger = stateEnum -> {
- // Stubbed.
- };
mTestThreadingDomain = new TestThreadingDomain();
+ mTestMetricsLogger = new TestMetricsLogger();
+
mTestCallback = new TestCallback(mTestThreadingDomain);
+
+ ProviderMetricsLogger stubbedProviderMetricsLogger = stateEnum -> {};
mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
stubbedProviderMetricsLogger, mTestThreadingDomain, "primary");
mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
@@ -94,11 +106,20 @@
}
@Test
+ public void controllerStartsInUnknownState() {
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
+ assertControllerState(controller, STATE_UNKNOWN);
+ }
+
+ @Test
public void initializationFailure_primary() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout()
.plus(testEnvironment.getProviderInitializationTimeoutFuzz());
@@ -106,25 +127,29 @@
// Initialize. After initialization the providers must be initialized and one should be
// started.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertInitialized();
mTestSecondaryLocationTimeZoneProvider.assertInitialized();
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout);
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void initializationFailure_secondary() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout()
.plus(testEnvironment.getProviderInitializationTimeoutFuzz());
@@ -132,387 +157,460 @@
// Initialize. After initialization the providers must be initialized and one should be
// started.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertInitialized();
mTestSecondaryLocationTimeZoneProvider.assertInitialized();
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void initializationFailure_both() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestPrimaryLocationTimeZoneProvider.setFailDuringInitialization(true);
mTestSecondaryLocationTimeZoneProvider.setFailDuringInitialization(true);
// Initialize. After initialization the providers must be initialized and one should be
// started.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertInitialized();
mTestSecondaryLocationTimeZoneProvider.assertInitialized();
+ assertControllerState(controller, STATE_FAILED);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING, STATE_FAILED);
mTestCallback.assertUncertainSuggestionMadeAndCommit();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void initialState_started() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout()
.plus(testEnvironment.getProviderInitializationTimeoutFuzz());
// Initialize. After initialization the providers must be initialized and one should be
// started.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertInitialized();
mTestSecondaryLocationTimeZoneProvider.assertInitialized();
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void initialState_disabled() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_DISABLED);
// Initialize. After initialization the providers must be initialized but neither should be
// started.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertInitialized();
mTestSecondaryLocationTimeZoneProvider.assertInitialized();
+ assertControllerState(controller, STATE_STOPPED);
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_PROVIDERS_INITIALIZING, STATE_STOPPED);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void enabled_uncertaintySuggestionSentIfNoEventReceived() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate time passing with no provider event being received from the primary.
mTestThreadingDomain.executeNext();
+ assertControllerState(controller, STATE_INITIALIZING);
// The primary should have reported uncertainty, which should trigger the controller to
// start the uncertainty timeout and start the secondary.
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate time passing with no provider event being received from either the primary or
// secondary.
mTestThreadingDomain.executeNext();
+ assertControllerState(controller, STATE_INITIALIZING);
// Now both initialization timeouts should have triggered. The uncertainty timeout should
// still not be triggered.
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Finally, the uncertainty timeout should cause the controller to make an uncertain
// suggestion.
mTestThreadingDomain.executeNext();
+ assertControllerState(controller, STATE_UNCERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_UNCERTAIN);
mTestCallback.assertUncertainSuggestionMadeAndCommit();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void enabled_eventReceivedBeforeInitializationTimeout() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void enabled_eventReceivedFromPrimaryAfterInitializationTimeout() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate time passing with no provider event being received from the primary.
mTestThreadingDomain.executeNext();
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made and the secondary to be shut down.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void enabled_eventReceivedFromSecondaryAfterInitializationTimeout() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate time passing with no provider event being received from the primary.
mTestThreadingDomain.executeNext();
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate a location event being received from the secondary provider. This should cause a
// suggestion to be made.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void enabled_repeatedPrimaryCertainty() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// A second, identical event should not cause another suggestion.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// And a third, different event should cause another suggestion.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void enabled_repeatedSecondaryCertainty() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate time passing with no provider event being received from the primary.
mTestThreadingDomain.executeNext();
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate a location event being received from the secondary provider. This should cause a
// suggestion to be made.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// A second, identical event should not cause another suggestion.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// And a third, different event should cause another suggestion.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void enabled_uncertaintyTriggersASuggestionAfterUncertaintyTimeout() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made and ensure the primary is considered initialized.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate an uncertain event being received from the primary provider. This should not
// cause a suggestion to be made straight away, but the uncertainty timeout should be
@@ -520,12 +618,14 @@
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate a location event being received from the secondary provider. This should cause a
// suggestion to be made, cancel the uncertainty timeout and ensure the secondary is
@@ -533,13 +633,15 @@
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate an uncertain event being received from the secondary provider. This should not
// cause a suggestion to be made straight away, but the uncertainty timeout should be
@@ -547,65 +649,77 @@
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate time passing. This means the uncertainty timeout should fire and the uncertain
// suggestion should be made.
mTestThreadingDomain.executeNext();
+ assertControllerState(controller, STATE_UNCERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_UNCERTAIN);
mTestCallback.assertUncertainSuggestionMadeFromEventAndCommit(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void enabled_briefUncertaintyTriggersNoSuggestion() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Uncertainty should not cause a suggestion to be made straight away, but the uncertainty
// timeout should be started and the secondary should be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// And a success event from the primary provider should cause the controller to make another
// suggestion, the uncertainty timeout should be cancelled and the secondary should be
@@ -613,81 +727,97 @@
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void configChanges_enableAndDisableWithNoPreviousSuggestion() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_DISABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_STOPPED);
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_PROVIDERS_INITIALIZING, STATE_STOPPED);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is disabled.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+ assertControllerState(controller, STATE_STOPPED);
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void configChanges_enableAndDisableWithPreviousSuggestion() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_DISABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_STOPPED);
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_PROVIDERS_INITIALIZING, STATE_STOPPED);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a success event being received from the primary provider.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is disabled.
// Because there had been a previous suggestion, the controller should withdraw it
@@ -695,27 +825,33 @@
// of the time zone.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+ assertControllerState(controller, STATE_STOPPED);
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_UNCERTAIN, STATE_STOPPED);
mTestCallback.assertUncertainSuggestionMadeAndCommit();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void configChanges_userSwitch_enabledToEnabled() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate the primary provider suggesting a time zone.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
@@ -723,18 +859,20 @@
// Receiving a "success" provider event should cause a suggestion to be made synchronously,
// and also clear the scheduled uncertainty suggestion.
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate the user change (but geo detection still enabled).
testEnvironment.simulateConfigChange(USER2_CONFIG_GEO_DETECTION_ENABLED);
// Confirm that the previous suggestion was overridden.
- mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ assertControllerState(controller, STATE_INITIALIZING);
// We expect the provider to end up in PROVIDER_STATE_STARTED_INITIALIZING, but it should
// have been stopped when the user changed.
@@ -744,129 +882,158 @@
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfig(
PROVIDER_STATE_STARTED_INITIALIZING, USER2_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_UNCERTAIN, STATE_STOPPED, STATE_INITIALIZING);
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void primaryPermFailure_secondaryEventsReceived() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a failure location event being received from the primary provider. This should
// cause the secondary to be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate uncertainty from the secondary.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// And a success event from the secondary provider should cause the controller to make
// another suggestion, the uncertainty timeout should be cancelled.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate uncertainty from the secondary.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
}
@Test
public void primaryPermFailure_disableAndEnable() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a failure location event being received from the primary provider. This should
// cause the secondary to be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is disabled.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+ assertControllerState(controller, STATE_STOPPED);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void secondaryPermFailure_primaryEventsReceived() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate an uncertain event from the primary. This will start the secondary, which will
// give this test the opportunity to simulate its failure. Then it will be possible to
@@ -874,61 +1041,73 @@
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate failure event from the secondary. This should just affect the secondary's state.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// And a success event from the primary provider should cause the controller to make
// a suggestion, the uncertainty timeout should be cancelled.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate uncertainty from the primary. The secondary cannot be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
}
@Test
public void secondaryPermFailure_disableAndEnable() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate an uncertain event from the primary. This will start the secondary, which will
// give this test the opportunity to simulate its failure. Then it will be possible to
@@ -936,97 +1115,117 @@
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Simulate failure event from the secondary. This should just affect the secondary's state.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+ assertUncertaintyTimeoutSet(testEnvironment, controller);
// Now signal a config change so that geo detection is disabled.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+ assertControllerState(controller, STATE_STOPPED);
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_STOPPED);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled. Only the primary can be
// started.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void bothPermFailure_disableAndEnable() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate a failure event from the primary. This will start the secondary.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestMetricsLogger.assertStateChangesAndCommit();
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate failure event from the secondary.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+ assertControllerState(controller, STATE_FAILED);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_FAILED);
mTestCallback.assertUncertainSuggestionMadeAndCommit();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
@Test
public void stateRecording() {
// The test provider enables state recording by default.
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, true /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial states.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
{
- LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
+ LocationTimeZoneManagerServiceState state = controller.getStateForTests();
+ assertEquals(STATE_INITIALIZING, state.getControllerState());
assertNull(state.getLastSuggestion());
+ assertControllerRecordedStates(state,
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
assertProviderStates(state.getPrimaryProviderStates(),
PROVIDER_STATE_STOPPED, PROVIDER_STATE_STARTED_INITIALIZING);
assertProviderStates(state.getSecondaryProviderStates(), PROVIDER_STATE_STOPPED);
}
- controllerImpl.clearRecordedProviderStates();
+ controller.clearRecordedStates();
// Simulate some provider behavior that will show up in the state recording.
@@ -1035,33 +1234,39 @@
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
{
- LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
+ LocationTimeZoneManagerServiceState state = controller.getStateForTests();
+ assertEquals(STATE_INITIALIZING, state.getControllerState());
assertNull(state.getLastSuggestion());
+ assertControllerRecordedStates(state);
assertProviderStates(
state.getPrimaryProviderStates(), PROVIDER_STATE_STARTED_UNCERTAIN);
assertProviderStates(
state.getSecondaryProviderStates(), PROVIDER_STATE_STARTED_INITIALIZING);
}
- controllerImpl.clearRecordedProviderStates();
+ controller.clearRecordedStates();
// Simulate a certain event from the secondary.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
{
- LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
+ LocationTimeZoneManagerServiceState state = controller.getStateForTests();
+ assertEquals(STATE_CERTAIN, state.getControllerState());
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
state.getLastSuggestion().getZoneIds());
+ assertControllerRecordedStates(state, STATE_CERTAIN);
assertProviderStates(state.getPrimaryProviderStates());
assertProviderStates(
state.getSecondaryProviderStates(), PROVIDER_STATE_STARTED_CERTAIN);
}
- controllerImpl.clearRecordedProviderStates();
+ controller.clearRecordedStates();
{
- LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
+ LocationTimeZoneManagerServiceState state = controller.getStateForTests();
+ assertEquals(STATE_CERTAIN, state.getControllerState());
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
state.getLastSuggestion().getZoneIds());
+ assertControllerRecordedStates(state);
assertProviderStates(state.getPrimaryProviderStates());
assertProviderStates(state.getSecondaryProviderStates());
}
@@ -1078,19 +1283,23 @@
@Test
public void destroy() {
- ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
- mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+ mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+ mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
TestEnvironment testEnvironment = new TestEnvironment(
- mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain, controller, USER1_CONFIG_GEO_DETECTION_ENABLED);
// Initialize and check initial state.
- controllerImpl.initialize(testEnvironment, mTestCallback);
+ controller.initialize(testEnvironment, mTestCallback);
+ assertControllerState(controller, STATE_INITIALIZING);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_PROVIDERS_INITIALIZING, STATE_STOPPED, STATE_INITIALIZING);
mTestCallback.assertNoSuggestionMade();
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Simulate the primary provider suggesting a time zone.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
@@ -1098,15 +1307,21 @@
// Receiving a "success" provider event should cause a suggestion to be made synchronously,
// and also clear the scheduled uncertainty suggestion.
+ assertControllerState(controller, STATE_CERTAIN);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestMetricsLogger.assertStateChangesAndCommit(STATE_CERTAIN);
mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
// Trigger destroy().
- controllerImpl.destroy();
+ controller.destroy();
+
+ assertControllerState(controller, STATE_DESTROYED);
+ mTestMetricsLogger.assertStateChangesAndCommit(
+ STATE_UNCERTAIN, STATE_STOPPED, STATE_DESTROYED);
// Confirm that the previous suggestion was overridden.
mTestCallback.assertUncertainSuggestionMadeAndCommit();
@@ -1115,7 +1330,7 @@
PROVIDER_STATE_STOPPED, PROVIDER_STATE_DESTROYED);
mTestSecondaryLocationTimeZoneProvider.assertStateChangesAndCommit(
PROVIDER_STATE_DESTROYED);
- assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ assertFalse(controller.isUncertaintyTimeoutSet());
}
private static void assertUncertaintyTimeoutSet(
@@ -1135,6 +1350,17 @@
.build());
}
+ private static void assertControllerState(LocationTimeZoneProviderController controller,
+ @State String expectedState) {
+ assertEquals(expectedState, controller.getStateForTests().getControllerState());
+ }
+
+ private static void assertControllerRecordedStates(
+ LocationTimeZoneManagerServiceState state,
+ @State String... expectedStates) {
+ assertEquals(Arrays.asList(expectedStates), state.getControllerStates());
+ }
+
private static class TestEnvironment extends LocationTimeZoneProviderController.Environment {
// These timeouts are set deliberately so that:
@@ -1206,6 +1432,22 @@
}
}
+ private static class TestMetricsLogger
+ implements LocationTimeZoneProviderController.MetricsLogger {
+
+ private final TestState<@State String> mLatestStateEnum = new TestState<>();
+
+ @Override
+ public void onStateChange(@State String stateEnum) {
+ mLatestStateEnum.set(stateEnum);
+ }
+
+ public void assertStateChangesAndCommit(@State String... expectedStateEnums) {
+ mLatestStateEnum.assertChanges(expectedStateEnums);
+ mLatestStateEnum.commitLatest();
+ }
+ }
+
private static class TestCallback extends LocationTimeZoneProviderController.Callback {
private TestState<GeolocationTimeZoneSuggestion> mLatestSuggestion = new TestState<>();
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index 16ac1d6..a2df313 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -41,14 +41,15 @@
}
private static ConfigurationInternal createUserConfig(
- @UserIdInt int userId, boolean geoDetectionEnabled) {
+ @UserIdInt int userId, boolean geoDetectionEnabledSetting) {
return new ConfigurationInternal.Builder(userId)
.setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
- .setAutoDetectionEnabled(true)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(geoDetectionEnabled)
+ .setTelephonyFallbackSupported(false)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(geoDetectionEnabledSetting)
.build();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
index 3716507..7eb6c97 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
@@ -16,7 +16,7 @@
package com.android.server.uri;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -121,47 +121,47 @@
LocalServices.addService(PackageManagerInternal.class, mPmInternal);
for (int userId : new int[] { USER_PRIMARY, USER_SECONDARY }) {
- when(mPmInternal.getPackageUid(eq(PKG_SOCIAL), anyInt(), eq(userId)))
+ when(mPmInternal.getPackageUid(eq(PKG_SOCIAL), anyLong(), eq(userId)))
.thenReturn(UserHandle.getUid(userId, UID_SOCIAL));
- when(mPmInternal.getPackageUid(eq(PKG_CAMERA), anyInt(), eq(userId)))
+ when(mPmInternal.getPackageUid(eq(PKG_CAMERA), anyLong(), eq(userId)))
.thenReturn(UserHandle.getUid(userId, UID_CAMERA));
- when(mPmInternal.getPackageUid(eq(PKG_PRIVATE), anyInt(), eq(userId)))
+ when(mPmInternal.getPackageUid(eq(PKG_PRIVATE), anyLong(), eq(userId)))
.thenReturn(UserHandle.getUid(userId, UID_PRIVATE));
- when(mPmInternal.getPackageUid(eq(PKG_PUBLIC), anyInt(), eq(userId)))
+ when(mPmInternal.getPackageUid(eq(PKG_PUBLIC), anyLong(), eq(userId)))
.thenReturn(UserHandle.getUid(userId, UID_PUBLIC));
- when(mPmInternal.getPackageUid(eq(PKG_FORCE), anyInt(), eq(userId)))
+ when(mPmInternal.getPackageUid(eq(PKG_FORCE), anyLong(), eq(userId)))
.thenReturn(UserHandle.getUid(userId, UID_FORCE));
- when(mPmInternal.getPackageUid(eq(PKG_COMPLEX), anyInt(), eq(userId)))
+ when(mPmInternal.getPackageUid(eq(PKG_COMPLEX), anyLong(), eq(userId)))
.thenReturn(UserHandle.getUid(userId, UID_COMPLEX));
- when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyLong(), eq(userId),
eq(Process.SYSTEM_UID)))
.thenReturn(buildCameraProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyLong(), eq(userId),
eq(UserHandle.getUid(userId, UID_CAMERA))))
.thenReturn(buildCameraProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_PRIVATE), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_PRIVATE), anyLong(), eq(userId),
eq(Process.SYSTEM_UID)))
.thenReturn(buildPrivateProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_PRIVATE), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_PRIVATE), anyLong(), eq(userId),
eq(UserHandle.getUid(userId, UID_PRIVATE))))
.thenReturn(buildPrivateProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_PUBLIC), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_PUBLIC), anyLong(), eq(userId),
eq(Process.SYSTEM_UID)))
.thenReturn(buildPublicProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_PUBLIC), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_PUBLIC), anyLong(), eq(userId),
eq(UserHandle.getUid(userId, UID_PUBLIC))))
.thenReturn(buildPublicProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_FORCE), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_FORCE), anyLong(), eq(userId),
eq(Process.SYSTEM_UID)))
.thenReturn(buildForceProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_FORCE), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_FORCE), anyLong(), eq(userId),
eq(UserHandle.getUid(userId, UID_FORCE))))
.thenReturn(buildForceProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_COMPLEX), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_COMPLEX), anyLong(), eq(userId),
eq(Process.SYSTEM_UID)))
.thenReturn(buildComplexProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_COMPLEX), anyInt(), eq(userId),
+ when(mPmInternal.resolveContentProvider(eq(PKG_COMPLEX), anyLong(), eq(userId),
eq(UserHandle.getUid(userId, UID_COMPLEX))))
.thenReturn(buildComplexProvider(userId));
}
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 9e46e1f..949ee01 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -63,6 +63,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -530,8 +531,8 @@
eq(UserHandle.getAppId(ai.uid)), eq(userIdForTest), anyLong()))
.thenReturn(idle[i]);
}
- when(mInjector.mPackageManagerInternal.getInstalledApplications(anyInt(), eq(userIdForTest),
- anyInt())).thenReturn(installedApps);
+ when(mInjector.mPackageManagerInternal.getInstalledApplications(anyLong(),
+ eq(userIdForTest), anyInt())).thenReturn(installedApps);
final int[] returnedIdleUids = controllerUnderTest.getIdleUidsForUser(userIdForTest);
assertEquals(expectedIdleUids.length, returnedIdleUids.length);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 7d24a2f..beee2a7 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -16,6 +16,19 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+import static android.os.Vibrator.VIBRATION_INTENSITY_HIGH;
+import static android.os.Vibrator.VIBRATION_INTENSITY_LOW;
+import static android.os.Vibrator.VIBRATION_INTENSITY_MEDIUM;
+import static android.os.Vibrator.VIBRATION_INTENSITY_OFF;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -39,9 +52,7 @@
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.UserHandle;
-import android.os.VibrationAttributes;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
@@ -70,16 +81,19 @@
public class VibrationSettingsTest {
private static final int UID = 1;
- private static final int USER_OPERATION_TIMEOUT_MILLIS = 60_000; // 1 min
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
.setBatterySaverEnabled(true).build();
- @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
- @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule
+ public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- @Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
- @Mock private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock
+ private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
+ @Mock
+ private PowerManagerInternal mPowerManagerInternalMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
@@ -112,7 +126,7 @@
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
}
@@ -127,16 +141,14 @@
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- verify(mListenerMock, times(7)).onChange();
+ verify(mListenerMock, times(8)).onChange();
}
@Test
@@ -171,89 +183,83 @@
VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
new Handler(mTestLooper.getLooper()));
- assertFalse(vibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_RINGTONE));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
+ assertFalse(vibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_HARDWARE_FEEDBACK));
}
@Test
public void shouldVibrateForRingerMode_withoutRingtoneUsage_returnsTrue() {
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_HARDWARE_FEEDBACK));
}
@Test
public void shouldVibrateForRingerMode_withVibrateWhenRinging_ignoreSettingsForSilentMode() {
- int usageRingtone = VibrationAttributes.USAGE_RINGTONE;
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForRingerMode_withApplyRampingRinger_ignoreSettingsForSilentMode() {
- int usageRingtone = VibrationAttributes.USAGE_RINGTONE;
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForRingerMode_withAllSettingsOff_onlyVibratesForVibrateMode() {
- int usageRingtone = VibrationAttributes.USAGE_RINGTONE;
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForUid_withForegroundOnlyUsage_returnsTrueWhInForeground() {
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH));
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
- assertFalse(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_TOUCH));
+ assertFalse(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH));
}
@Test
@@ -261,38 +267,32 @@
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID,
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID,
- VibrationAttributes.USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_RINGTONE));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_RINGTONE));
}
@Test
public void shouldVibrateForPowerMode_withLowPowerAndAllowedUsage_returnTrue() {
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(VibrationAttributes.USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(
- VibrationAttributes.USAGE_RINGTONE));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_RINGTONE));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_COMMUNICATION_REQUEST));
}
@Test
public void shouldVibrateForPowerMode_withRestrictedUsage_returnsFalseWhileInLowPowerMode() {
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(VibrationAttributes.USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(
- VibrationAttributes.USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION));
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- assertFalse(mVibrationSettings.shouldVibrateForPowerMode(VibrationAttributes.USAGE_TOUCH));
- assertFalse(mVibrationSettings.shouldVibrateForPowerMode(
- VibrationAttributes.USAGE_NOTIFICATION));
+ assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH));
+ assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION));
}
@Test
@@ -324,108 +324,128 @@
@Test
public void getDefaultIntensity_beforeSystemReady_returnsMediumToAllExceptAlarm() {
- mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
- mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_HIGH);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
new Handler(mTestLooper.getLooper()));
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_ALARM));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_UNKNOWN));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(
- VibrationAttributes.USAGE_PHYSICAL_EMULATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ vibrationSettings.getDefaultIntensity(USAGE_ALARM));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_TOUCH));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_UNKNOWN));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_RINGTONE));
}
@Test
public void getDefaultIntensity_returnsIntensityFromVibratorService() {
- mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
- mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_MEDIUM);
+ mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_ALARM));
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_UNKNOWN));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getDefaultIntensity(
- VibrationAttributes.USAGE_PHYSICAL_EMULATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getDefaultIntensity(USAGE_ALARM));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getDefaultIntensity(USAGE_TOUCH));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getDefaultIntensity(USAGE_UNKNOWN));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getDefaultIntensity(USAGE_RINGTONE));
}
@Test
public void getCurrentIntensity_returnsIntensityFromSettings() {
- mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
- mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_LOW);
+ VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_ALARM));
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_UNKNOWN));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getCurrentIntensity(
- VibrationAttributes.USAGE_PHYSICAL_EMULATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+ assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_ALARM));
+ assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getCurrentIntensity(USAGE_NOTIFICATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getCurrentIntensity(USAGE_UNKNOWN));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
}
@Test
public void getCurrentIntensity_updateTriggeredAfterUserSwitched() {
- mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_HIGH);
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+ mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
// Switching user is not working with FakeSettingsProvider.
// Testing the broadcast flow manually.
Settings.System.putIntForUser(mContextSpy.getContentResolver(),
- Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW,
+ Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
UserHandle.USER_CURRENT);
mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
new Intent(Intent.ACTION_USER_SWITCHED));
- assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
+ }
+
+ @Test
+ public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() {
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
+ // If haptic feedback is off, fallback to default value.
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
+
+ // Switching user is not working with FakeSettingsProvider.
+ // Testing the broadcast flow manually.
+ Settings.System.putIntForUser(mContextSpy.getContentResolver(),
+ Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH,
+ UserHandle.USER_CURRENT);
+ mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
+ new Intent(Intent.ACTION_USER_SWITCHED));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index be83efb..c0f7596 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -126,15 +126,23 @@
new VibrationAttributes.Builder().setUsage(
VibrationAttributes.USAGE_RINGTONE).build();
- @Rule public MockitoRule rule = MockitoJUnit.rule();
- @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+ @Rule
+ public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- @Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock;
- @Mock private PackageManagerInternal mPackageManagerInternalMock;
- @Mock private PowerManagerInternal mPowerManagerInternalMock;
- @Mock private PowerSaveState mPowerSaveStateMock;
- @Mock private AppOpsManager mAppOpsManagerMock;
- @Mock private IInputManager mIInputManagerMock;
+ @Mock
+ private VibratorManagerService.NativeWrapper mNativeWrapperMock;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternalMock;
+ @Mock
+ private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock
+ private PowerSaveState mPowerSaveStateMock;
+ @Mock
+ private AppOpsManager mAppOpsManagerMock;
+ @Mock
+ private IInputManager mIInputManagerMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -397,6 +405,7 @@
@Test
public void registerVibratorStateListener_multipleVibratorsAreTriggered() throws Exception {
mockVibrators(0, 1, 2);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
IVibratorStateListener[] listeners = new IVibratorStateListener[3];
for (int i = 0; i < 3; i++) {
@@ -536,7 +545,7 @@
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
VibratorManagerService service = createSystemReadyService();
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
// Wait before checking it never played.
@@ -544,14 +553,14 @@
service, /* timeout= */ 50));
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
service = createSystemReadyService();
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
service, TEST_TIMEOUT_MILLIS));
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
service = createSystemReadyService();
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
@@ -601,8 +610,8 @@
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY).build();
- VibrationAttributes vibrationAttributes = new VibrationAttributes.Builder(
- audioAttributes, effect).build();
+ VibrationAttributes vibrationAttributes =
+ new VibrationAttributes.Builder(audioAttributes).build();
vibrate(service, effect, vibrationAttributes);
@@ -621,7 +630,7 @@
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
new VibrationAttributes.Builder().setUsage(
VibrationAttributes.USAGE_COMMUNICATION_REQUEST).build());
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
+ vibrate(service, VibrationEffect.createOneShot(2000, 200),
new VibrationAttributes.Builder().setUsage(
VibrationAttributes.USAGE_UNKNOWN).build());
@@ -635,13 +644,67 @@
inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST),
+ eq(AudioAttributes.USAGE_VOICE_COMMUNICATION),
anyInt(), anyString());
inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
}
@Test
+ public void vibrate_withAttributesUnknownUsage_usesEffectToIdentifyTouchUsage() {
+ VibratorManagerService service = createSystemReadyService();
+
+ VibrationAttributes unknownAttributes = VibrationAttributes.createForUsage(
+ VibrationAttributes.USAGE_UNKNOWN);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), unknownAttributes);
+ vibrate(service, VibrationEffect.createOneShot(200, 200), unknownAttributes);
+ vibrate(service, VibrationEffect.createWaveform(
+ new long[] { 100, 200, 300 }, new int[] {1, 2, 3}, -1), unknownAttributes);
+ vibrate(service,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL)
+ .compose(),
+ unknownAttributes);
+
+ verify(mAppOpsManagerMock, times(4))
+ .checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
+ verify(mAppOpsManagerMock, never())
+ .checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
+ }
+
+ @Test
+ public void vibrate_withAttributesUnknownUsage_ignoresEffectIfNotHapticFeedbackCandidate() {
+ VibratorManagerService service = createSystemReadyService();
+
+ VibrationAttributes unknownAttributes = VibrationAttributes.createForUsage(
+ VibrationAttributes.USAGE_UNKNOWN);
+ vibrate(service, VibrationEffect.get(VibrationEffect.RINGTONES[0]), unknownAttributes);
+ vibrate(service, VibrationEffect.createOneShot(2000, 200), unknownAttributes);
+ vibrate(service, VibrationEffect.createWaveform(
+ new long[] { 100, 200, 300 }, new int[] {1, 2, 3}, 0), unknownAttributes);
+ vibrate(service,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .compose(),
+ unknownAttributes);
+
+ verify(mAppOpsManagerMock, never())
+ .checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
+ verify(mAppOpsManagerMock, times(4))
+ .checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
+ }
+
+ @Test
public void vibrate_withOngoingRepeatingVibration_ignoresEffect() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -1109,19 +1172,19 @@
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
createSystemReadyService();
int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
createSystemReadyService();
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertEquals(IExternalVibratorService.SCALE_NONE, scale);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
createSystemReadyService();
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertEquals(IExternalVibratorService.SCALE_NONE, scale);
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
index fdaf7cc..1bc4775 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.android.servicestests.apps.simpleservicetestapp">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<service android:name=".SimpleService"
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
index ae46f52..8270583 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
@@ -17,8 +17,10 @@
import android.app.Service;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -33,6 +35,9 @@
private static final String TEST_CLASS =
"com.android.servicestests.apps.simpleservicetestapp.SimpleService";
+ private static final String ACTION_SERVICE_WITH_DEP_PKG =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
+
private static final String EXTRA_CALLBACK = "callback";
private static final String EXTRA_COMMAND = "command";
private static final String EXTRA_FLAGS = "flags";
@@ -121,6 +126,21 @@
@Override
public IBinder onBind(Intent intent) {
+ if (ACTION_SERVICE_WITH_DEP_PKG.equals(intent.getAction())) {
+ final String targetPkg = intent.getStringExtra(EXTRA_TARGET_PACKAGE);
+ Log.i(TAG, "SimpleService.onBind: " + ACTION_SERVICE_WITH_DEP_PKG + " " + targetPkg);
+ if (targetPkg != null) {
+ Context pkgContext = null;
+ try {
+ pkgContext = createPackageContext(targetPkg,
+ Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to create package context for " + pkgContext, e);
+ }
+ // This effectively loads the target package as a dependency.
+ pkgContext.getClassLoader();
+ }
+ }
return mBinder;
}
}
diff --git a/services/tests/servicestests/test-apps/StubApp/Android.bp b/services/tests/servicestests/test-apps/StubApp/Android.bp
new file mode 100644
index 0000000..99deb3f
--- /dev/null
+++ b/services/tests/servicestests/test-apps/StubApp/Android.bp
@@ -0,0 +1,37 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "StubTestApp",
+
+ sdk_version: "current",
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml
new file mode 100644
index 0000000..90172e7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.stubapp">
+
+ <application android:label="StubTestApp">
+ <activity android:name=".TestActivity"
+ android:exported="true" />
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java
similarity index 76%
copy from core/java/android/window/TaskFragmentAppearedInfo.aidl
copy to services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java
index 3729c09..0d94676 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package android.window;
+package com.android.servicestests.apps.stubapp;
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+import android.app.Activity;
+
+public class TestActivity extends Activity {
+}
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 cdb7230..2f054b0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -26,6 +26,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -809,7 +810,7 @@
service.isComponentEnabledForCurrentProfiles(
unapprovedAdditionalComponent));
verify(mIpm, never()).getServiceInfo(
- eq(unapprovedAdditionalComponent), anyInt(), anyInt());
+ eq(unapprovedAdditionalComponent), anyLong(), anyInt());
}
}
@@ -953,7 +954,7 @@
service.isComponentEnabledForCurrentProfiles(
unapprovedAdditionalComponent));
verify(mIpm, never()).getServiceInfo(
- eq(unapprovedAdditionalComponent), anyInt(), anyInt());
+ eq(unapprovedAdditionalComponent), anyLong(), anyInt());
}
}
@@ -1702,14 +1703,14 @@
assertTrue(service.isComponentEnabledForCurrentProfiles(
componentName));
verify(mIpm, times(1)).getServiceInfo(
- eq(componentName), anyInt(), anyInt());
+ eq(componentName), anyLong(), anyInt());
}
} else {
ComponentName componentName =
ComponentName.unflattenFromString(packageOrComponent);
assertTrue(service.isComponentEnabledForCurrentProfiles(componentName));
verify(mIpm, times(1)).getServiceInfo(
- eq(componentName), anyInt(), anyInt());
+ eq(componentName), anyLong(), anyInt());
}
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 5808964..837850f 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -99,6 +99,9 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
@@ -119,6 +122,7 @@
import android.app.StatsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
+import android.companion.AssociationInfo;
import android.companion.ICompanionDeviceManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -393,7 +397,7 @@
// MockPackageManager - default returns ApplicationInfo with matching calling UID
mContext.setMockPackageManager(mPackageManagerClient);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), anyInt()))
.thenAnswer((Answer<ApplicationInfo>) invocation -> {
Object[] args = invocation.getArguments();
return getApplicationInfo((String) args[0], mUid);
@@ -2335,10 +2339,8 @@
@Test
public void testCreateChannelNotifyListener() throws Exception {
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
@@ -2364,10 +2366,8 @@
@Test
public void testCreateChannelGroupNotifyListener() throws Exception {
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mService.setPreferencesHelper(mPreferencesHelper);
NotificationChannelGroup group1 = new NotificationChannelGroup("a", "b");
NotificationChannelGroup group2 = new NotificationChannelGroup("n", "m");
@@ -2385,10 +2385,8 @@
@Test
public void testUpdateChannelNotifyListener() throws Exception {
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mService.setPreferencesHelper(mPreferencesHelper);
mTestNotificationChannel.setLightColor(Color.CYAN);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
@@ -2404,10 +2402,8 @@
@Test
public void testDeleteChannelNotifyListener() throws Exception {
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
@@ -2423,10 +2419,8 @@
@Test
public void testDeleteChannelOnlyDoExtraWorkIfExisted() throws Exception {
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
@@ -2439,10 +2433,8 @@
@Test
public void testDeleteChannelGroupNotifyListener() throws Exception {
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
@@ -2457,10 +2449,8 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
@@ -2479,9 +2469,8 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(emptyList());
try {
mBinderService.updateNotificationChannelFromPrivilegedListener(
@@ -2502,10 +2491,8 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mListener = mock(ManagedServices.ManagedServiceInfo.class);
mListener.component = new ComponentName(PKG, PKG);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
@@ -2530,10 +2517,8 @@
@Test
public void testGetNotificationChannelFromPrivilegedListener_cdm_success() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mBinderService.getNotificationChannelsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
@@ -2545,9 +2530,8 @@
@Test
public void testGetNotificationChannelFromPrivilegedListener_cdm_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(emptyList());
try {
mBinderService.getNotificationChannelsFromPrivilegedListener(
@@ -2566,7 +2550,7 @@
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(new ArrayList<>());
+ .thenReturn(emptyList());
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
mBinderService.getNotificationChannelsFromPrivilegedListener(
@@ -2581,7 +2565,7 @@
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(new ArrayList<>());
+ .thenReturn(emptyList());
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
try {
@@ -2599,10 +2583,8 @@
@Test
public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
@@ -2622,10 +2604,8 @@
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
- associations.add("a");
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
@@ -2636,9 +2616,8 @@
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(emptyList());
try {
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
@@ -2654,9 +2633,8 @@
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
- .thenReturn(associations);
+ .thenReturn(emptyList());
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
@@ -5065,7 +5043,7 @@
public void testIsCallerInstantApp_primaryUser() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), eq(0))).thenReturn(info);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
assertTrue(mService.isCallerInstantApp(45770, 0));
@@ -5078,8 +5056,8 @@
public void testIsCallerInstantApp_secondaryUser() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), eq(10))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), eq(0))).thenReturn(null);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
assertTrue(mService.isCallerInstantApp(68638450, 10));
@@ -5089,7 +5067,7 @@
public void testIsCallerInstantApp_userAllNotification() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(USER_SYSTEM)))
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), eq(USER_SYSTEM)))
.thenReturn(info);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
@@ -5103,8 +5081,8 @@
public void testResolveNotificationUid_sameApp_nonSystemUser() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.uid = Binder.getCallingUid();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), eq(10))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), eq(0))).thenReturn(null);
int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 10);
@@ -5115,7 +5093,7 @@
public void testResolveNotificationUid_sameApp() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.uid = Binder.getCallingUid();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), eq(0))).thenReturn(info);
int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 0);
@@ -5126,7 +5104,7 @@
public void testResolveNotificationUid_sameAppDiffPackage() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.uid = Binder.getCallingUid();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), eq(0))).thenReturn(info);
int actualUid = mService.resolveNotificationUid("caller", "callerAlso", info.uid, 0);
@@ -5137,7 +5115,7 @@
public void testResolveNotificationUid_sameAppWrongUid() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.uid = 1356347;
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), anyInt())).thenReturn(info);
try {
mService.resolveNotificationUid("caller", "caller", 9, 0);
@@ -5176,7 +5154,7 @@
PackageManager.NameNotFoundException.class);
ApplicationInfo ai = new ApplicationInfo();
ai.uid = -1;
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), anyInt())).thenReturn(ai);
final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
try {
@@ -5196,7 +5174,7 @@
PackageManager.NameNotFoundException.class);
ApplicationInfo ai = new ApplicationInfo();
ai.uid = -1;
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), anyInt())).thenReturn(ai);
// unlike the post case, ignore instead of throwing
final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
@@ -7326,12 +7304,12 @@
// Make sure the shortcut is cached.
verify(mShortcutServiceInternal).cacheShortcuts(
- anyInt(), any(), eq(PKG), eq(Collections.singletonList(VALID_CONVO_SHORTCUT_ID)),
+ anyInt(), any(), eq(PKG), eq(singletonList(VALID_CONVO_SHORTCUT_ID)),
eq(USER_SYSTEM), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
// Test: Remove the shortcut
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
- launcherAppsCallback.getValue().onShortcutsChanged(PKG, Collections.emptyList(),
+ launcherAppsCallback.getValue().onShortcutsChanged(PKG, emptyList(),
UserHandle.getUserHandleForUid(mUid));
waitForIdle();
@@ -7399,7 +7377,7 @@
// Make sure the shortcut is cached.
verify(mShortcutServiceInternal).cacheShortcuts(
- anyInt(), any(), eq(PKG), eq(Collections.singletonList(shortcutId)),
+ anyInt(), any(), eq(PKG), eq(singletonList(shortcutId)),
eq(USER_SYSTEM), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
// Test: Remove the notification
@@ -8511,6 +8489,18 @@
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ // using the style, but incorrect type in session - blocked
+ nb.setStyle(new Notification.MediaStyle());
+ Bundle extras = new Bundle();
+ extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, new Intent());
+ nb.addExtras(extras);
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
// style + media session - bypasses block
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index ea01963..2e5cf3c 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -44,6 +44,7 @@
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
@@ -89,6 +90,7 @@
import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
@@ -306,7 +308,7 @@
// MockPackageManager - default returns ApplicationInfo with matching calling UID
mContext.setMockPackageManager(mPackageManagerClient);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
+ when(mPackageManager.getApplicationInfo(anyString(), anyLong(), anyInt()))
.thenAnswer((Answer<ApplicationInfo>) invocation -> {
Object[] args = invocation.getArguments();
return getApplicationInfo((String) args[0], mUid);
@@ -746,6 +748,18 @@
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ // using the style, but incorrect type in session - blocked
+ nb.setStyle(new Notification.MediaStyle());
+ Bundle extras = new Bundle();
+ extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, new Intent());
+ nb.addExtras(extras);
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
// style + media session - bypasses block
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 4cdae88..5800400 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
@@ -42,7 +43,6 @@
import android.permission.IPermissionManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
-import android.util.Slog;
import androidx.test.runner.AndroidJUnit4;
@@ -169,7 +169,8 @@
ParceledListSlice<PackageInfo> infos = new ParceledListSlice<>(
ImmutableList.of(notThis, none, first, second));
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS), anyInt())).thenReturn(infos);
+ when(mPackageManager.getInstalledPackages(eq((long) GET_PERMISSIONS), anyInt()))
+ .thenReturn(infos);
Set<Pair<Integer, String>> actual = mPermissionHelper.getAppsRequestingPermission(0);
@@ -181,7 +182,7 @@
int userId = 1;
ParceledListSlice<PackageInfo> infos = ParceledListSlice.emptyList();
when(mPackageManager.getPackagesHoldingPermissions(
- eq(new String[] {Manifest.permission.POST_NOTIFICATIONS}), anyInt(), eq(userId)))
+ eq(new String[] {Manifest.permission.POST_NOTIFICATIONS}), anyLong(), eq(userId)))
.thenReturn(infos);
assertThat(mPermissionHelper.getAppsGrantedPermission(userId)).isNotNull();
}
@@ -206,7 +207,7 @@
ParceledListSlice<PackageInfo> infos = new ParceledListSlice<>(
ImmutableList.of(first, second));
when(mPackageManager.getPackagesHoldingPermissions(
- eq(new String[] {Manifest.permission.POST_NOTIFICATIONS}), anyInt(), eq(userId)))
+ eq(new String[] {Manifest.permission.POST_NOTIFICATIONS}), anyLong(), eq(userId)))
.thenReturn(infos);
Set<Pair<Integer, String>> expected =
@@ -305,11 +306,11 @@
ParceledListSlice<PackageInfo> infos = new ParceledListSlice<>(
ImmutableList.of(first, second));
when(mPackageManager.getPackagesHoldingPermissions(
- eq(new String[] {Manifest.permission.POST_NOTIFICATIONS}), anyInt(), eq(userId)))
+ eq(new String[] {Manifest.permission.POST_NOTIFICATIONS}), anyLong(), eq(userId)))
.thenReturn(infos);
ParceledListSlice<PackageInfo> requesting = new ParceledListSlice<>(
ImmutableList.of(first, second, third));
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS), anyInt()))
+ when(mPackageManager.getInstalledPackages(eq((long) GET_PERMISSIONS), anyInt()))
.thenReturn(requesting);
Map<Pair<Integer, String>, Boolean> expected = ImmutableMap.of(new Pair(1, "first"), true,
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 ae24785..7a133ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1778,11 +1778,6 @@
anyInt() /* orientation */, anyInt() /* lastRotation */);
// Set to visible so the activity can freeze the screen.
activity.setVisibility(true);
- // Update the display policy to make the screen fully turned on so the freeze is allowed
- display.getDisplayPolicy().screenTurnedOn(null);
- display.getDisplayPolicy().finishKeyguardDrawn();
- display.getDisplayPolicy().finishWindowsDrawn();
- display.getDisplayPolicy().finishScreenTurningOn();
display.rotateInDifferentOrientationIfNeeded(activity);
display.setFixedRotationLaunchingAppUnchecked(activity);
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 0cab911..e2f0658 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -63,6 +63,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
@@ -352,7 +353,7 @@
doReturn(null).when(mMockPackageManager).getDefaultHomeActivity(anyInt());
doReturn(mMockPackageManager).when(mAtm).getPackageManagerInternalLocked();
doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any());
- doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyInt(), anyInt(),
+ doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyLong(), anyLong(),
anyInt(), anyBoolean(), anyInt());
doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent();
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 1266b2e..e0072b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -35,6 +35,7 @@
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;
@@ -63,6 +64,8 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.IDisplayWindowListener;
import androidx.test.filters.MediumTest;
@@ -220,6 +223,66 @@
assertEquals(1, removed.size());
}
+ @Test
+ public void testSetLockScreenShownWithVirtualDisplay() {
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.copyFrom(mDisplayInfo);
+ displayInfo.type = Display.TYPE_VIRTUAL;
+ DisplayContent virtualDisplay = createNewDisplay(displayInfo);
+
+ // Make sure we're starting out with 2 unlocked displays
+ assertEquals(2, mRootWindowContainer.getChildCount());
+ mRootWindowContainer.forAllDisplays(displayContent -> {
+ assertFalse(displayContent.isKeyguardLocked());
+ assertFalse(displayContent.isAodShowing());
+ });
+
+ // Check that setLockScreenShown locks both displays
+ mAtm.setLockScreenShown(true, true);
+ mRootWindowContainer.forAllDisplays(displayContent -> {
+ assertTrue(displayContent.isKeyguardLocked());
+ assertTrue(displayContent.isAodShowing());
+ });
+
+ // Check setLockScreenShown unlocking both displays
+ mAtm.setLockScreenShown(false, false);
+ mRootWindowContainer.forAllDisplays(displayContent -> {
+ assertFalse(displayContent.isKeyguardLocked());
+ assertFalse(displayContent.isAodShowing());
+ });
+ }
+
+ @Test
+ public void testSetLockScreenShownWithAlwaysUnlockedVirtualDisplay() {
+ assertEquals(Display.DEFAULT_DISPLAY, mRootWindowContainer.getChildAt(0).getDisplayId());
+
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.copyFrom(mDisplayInfo);
+ displayInfo.type = Display.TYPE_VIRTUAL;
+ displayInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ displayInfo.flags = Display.FLAG_OWN_DISPLAY_GROUP | Display.FLAG_ALWAYS_UNLOCKED;
+ DisplayContent newDisplay = createNewDisplay(displayInfo);
+
+ // Make sure we're starting out with 2 unlocked displays
+ assertEquals(2, mRootWindowContainer.getChildCount());
+ mRootWindowContainer.forAllDisplays(displayContent -> {
+ assertFalse(displayContent.isKeyguardLocked());
+ assertFalse(displayContent.isAodShowing());
+ });
+
+ // setLockScreenShown should only lock the default display, not the virtual one
+ mAtm.setLockScreenShown(true, true);
+
+ assertTrue(mDefaultDisplay.isKeyguardLocked());
+ assertTrue(mDefaultDisplay.isAodShowing());
+
+ DisplayContent virtualDisplay = mRootWindowContainer.getDisplayContent(
+ newDisplay.getDisplayId());
+ assertNotEquals(Display.DEFAULT_DISPLAY, virtualDisplay.getDisplayId());
+ assertFalse(virtualDisplay.isKeyguardLocked());
+ assertFalse(virtualDisplay.isAodShowing());
+ }
+
/*
a test to verify b/144045134 - ignore PIP mode request for destroyed activity.
mocks r.getParent() to return null to cause NPE inside enterPipRunnable#run() in
@@ -854,12 +917,19 @@
ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
mAtm.mInternal.createPackageConfigurationUpdater(DEFAULT_PACKAGE_NAME,
DEFAULT_USER_ID);
+
+ // committing empty locales, when no config is set should return false.
+ assertFalse(packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList()).commit());
+
// committing new configuration returns true;
assertTrue(packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
.commit());
// applying the same configuration returns false.
assertFalse(packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
.commit());
+
+ // committing empty locales and undefined nightMode should return true (deletes the
+ // pre-existing record) if some config was previously set.
assertTrue(packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList())
.setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 5d0e34a..5062706 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -334,6 +334,18 @@
}
@Test
+ public void testExitAnimationDone_beforeAppTransition() {
+ final Task task = createTask(mDisplayContent);
+ final WindowState win = createAppWindow(task, ACTIVITY_TYPE_STANDARD, "Win");
+ spyOn(win);
+ win.mAnimatingExit = true;
+ mDisplayContent.mAppTransition.setTimeout();
+ mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+ verify(win).onExitAnimationDone();
+ }
+
+ @Test
public void testGetAnimationTargets_windowsAreBeingReplaced() {
// [DisplayContent] -+- [Task1] - [ActivityRecord1] (opening, visible)
// +- [AppWindow1] (being-replaced)
@@ -803,6 +815,7 @@
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
openingActivity.allDrawn = true;
+ task.effectiveUid = openingActivity.getUid();
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
@@ -879,6 +892,7 @@
final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
closingActivity.allDrawn = true;
closingActivity.info.applicationInfo.uid = 12345;
+ task.effectiveUid = closingActivity.getUid();
// Opening non-embedded activity with different UID.
final ActivityRecord openingActivity = createActivityRecord(task);
openingActivity.info.applicationInfo.uid = 54321;
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 08be15e..d7a0ab3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -681,7 +681,7 @@
final int maxWidth = 300;
final int resultingHeight = (maxWidth * baseHeight) / baseWidth;
- final int resultingDensity = baseDensity;
+ final int resultingDensity = (baseDensity * maxWidth) / baseWidth;
displayContent.setMaxUiWidth(maxWidth);
verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
@@ -756,6 +756,33 @@
}
@Test
+ public void testSetForcedDensity() {
+ final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
+ final int baseWidth = 1280;
+ final int baseHeight = 720;
+ final int baseDensity = 320;
+
+ displayContent.mInitialDisplayWidth = baseWidth;
+ displayContent.mInitialDisplayHeight = baseHeight;
+ displayContent.mInitialDisplayDensity = baseDensity;
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+
+ final int forcedDensity = 600;
+
+ // Verify that forcing the density is honored and the size doesn't change.
+ displayContent.setForcedDensity(forcedDensity, 0 /* userId */);
+ verifySizes(displayContent, baseWidth, baseHeight, forcedDensity);
+
+ // Verify that forcing the density is idempotent.
+ displayContent.setForcedDensity(forcedDensity, 0 /* userId */);
+ verifySizes(displayContent, baseWidth, baseHeight, forcedDensity);
+
+ // Verify that forcing resolution won't affect the already forced density.
+ displayContent.setForcedSize(1800, 1200);
+ verifySizes(displayContent, 1800, 1200, forcedDensity);
+ }
+
+ @Test
public void testDisplayCutout_rot0() {
final DisplayContent dc = createNewDisplay();
dc.mInitialDisplayWidth = 200;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index a8ede13..407f9cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_ON;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -29,7 +30,9 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -44,6 +47,7 @@
import com.android.server.inputmethod.InputMethodManagerService;
import com.android.server.inputmethod.InputMethodMenuController;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,18 +66,24 @@
private InputMethodMenuController mController;
private DualDisplayAreaGroupPolicyTest.DualDisplayContent mSecondaryDisplay;
+ private IWindowManager mIWindowManager;
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+
@Before
public void setUp() throws Exception {
- // Let the Display to be created with the DualDisplay policy.
+ // Let the Display be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
+ mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
+ .Builder(mAtm, 1000, 1000).build();
+ mSecondaryDisplay.getDisplayInfo().state = STATE_ON;
// Mock addWindowTokenWithOptions to create a test window token.
- IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
- spyOn(wms);
+ mIWindowManager = WindowManagerGlobal.getWindowManagerService();
+ spyOn(mIWindowManager);
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
IBinder clientToken = (IBinder) args[0];
@@ -83,19 +93,24 @@
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
null /* options */);
return dc.getImeContainer().getConfiguration();
- }).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
- anyInt(), any());
-
- mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
- .Builder(mAtm, 1000, 1000).build();
-
- // Mock DisplayManagerGlobal to return test display when obtaining Display instance.
+ }).when(mIWindowManager).attachWindowContextToDisplayArea(any(),
+ eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any());
+ mDisplayManagerGlobal = DisplayManagerGlobal.getInstance();
+ spyOn(mDisplayManagerGlobal);
final int displayId = mSecondaryDisplay.getDisplayId();
final Display display = mSecondaryDisplay.getDisplay();
- DisplayManagerGlobal displayManagerGlobal = DisplayManagerGlobal.getInstance();
- spyOn(displayManagerGlobal);
- doReturn(display).when(displayManagerGlobal).getCompatibleDisplay(eq(displayId),
+ doReturn(display).when(mDisplayManagerGlobal).getCompatibleDisplay(eq(displayId),
(Resources) any());
+ Context systemUiContext = ActivityThread.currentActivityThread()
+ .getSystemUiContext(displayId);
+ spyOn(systemUiContext);
+ doReturn(display).when(systemUiContext).getDisplay();
+ }
+
+ @After
+ public void tearDown() {
+ reset(mIWindowManager);
+ reset(mDisplayManagerGlobal);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index db60b98..9a33e23 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -266,6 +266,7 @@
doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt());
doNothing().when(amInternal).broadcastGlobalConfigurationChanged(anyInt(), anyBoolean());
doNothing().when(amInternal).cleanUpServices(anyInt(), any(), any());
+ doNothing().when(amInternal).reportCurKeyguardUsageEvent(anyBoolean());
doReturn(UserHandle.USER_SYSTEM).when(amInternal).getCurrentUserId();
doReturn(TEST_USER_PROFILE_IDS).when(amInternal).getCurrentProfileIds();
doReturn(true).when(amInternal).isUserRunning(anyInt(), anyInt());
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 7e5414e..a5c6dc0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -352,28 +353,66 @@
}
@Test
- public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
+ public void testApplyTransaction_enforceHierarchyChange_createTaskFragment()
+ throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final int uid = Binder.getCallingUid();
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ final IBinder fragmentToken = new Binder();
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token).build();
mOrganizer.applyTransaction(mTransaction);
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
- final TaskFragmentCreationParams mockParams = mock(TaskFragmentCreationParams.class);
- doReturn(mOrganizerToken).when(mockParams).getOrganizer();
- mTransaction.createTaskFragment(mockParams);
+ mTransaction.createTaskFragment(params);
mTransaction.startActivityInTaskFragment(
mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */);
mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class),
null /* options */);
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
- // testing the security check here.
- assertThrows(IllegalArgumentException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ // Successfully created a TaskFragment.
+ final TaskFragment taskFragment = mAtm.mWindowOrganizerController
+ .getTaskFragment(fragmentToken);
+ assertNotNull(taskFragment);
+ assertEquals(activity.getTask(), taskFragment.getTask());
+ }
+
+ @Test
+ public void testApplyTransaction_createTaskFragment_failForDifferentUid()
+ throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final int uid = Binder.getCallingUid();
+ final IBinder fragmentToken = new Binder();
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token).build();
+ mOrganizer.applyTransaction(mTransaction);
+ mTransaction.createTaskFragment(params);
+
+ // Fail to create TaskFragment when the task uid is different from caller.
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid + 1;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+
+ // Fail to create TaskFragment when the task uid is different from owner activity.
+ activity.info.applicationInfo.uid = uid + 1;
+ activity.getTask().effectiveUid = uid;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+
+ // Successfully created a TaskFragment for same uid.
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
}
@Test
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 6737b1a..730275c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -16,11 +16,14 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.clearInvocations;
import android.graphics.Rect;
@@ -91,6 +94,7 @@
final Rect endBounds = new Rect(500, 500, 1000, 1000);
mTaskFragment.setBounds(startBounds);
doReturn(true).when(mTaskFragment).isVisible();
+ doReturn(true).when(mTaskFragment).isVisibleRequested();
clearInvocations(mTransaction);
mTaskFragment.setBounds(endBounds);
@@ -108,6 +112,25 @@
verify(mTransaction).setWindowCrop(mLeash, 500, 500);
}
+ @Test
+ public void testNotOkToAnimate_doNotStartChangeTransition() {
+ mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer);
+ final Rect startBounds = new Rect(0, 0, 1000, 1000);
+ final Rect endBounds = new Rect(500, 500, 1000, 1000);
+ mTaskFragment.setBounds(startBounds);
+ doReturn(true).when(mTaskFragment).isVisible();
+ doReturn(true).when(mTaskFragment).isVisibleRequested();
+
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ displayPolicy.screenTurnedOff();
+
+ assertFalse(mTaskFragment.okToAnimate());
+
+ mTaskFragment.setBounds(endBounds);
+
+ verify(mTaskFragment, never()).initializeChangeTransition(any());
+ }
+
/**
* Tests that when a {@link TaskFragmentInfo} is generated from a {@link TaskFragment}, an
* activity that has not yet been attached to a process because it is being initialized but
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 c0ae8a5..3065e7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -168,6 +168,11 @@
doReturn(false).when(displayPolicy).hasStatusBar();
doReturn(false).when(newDisplay).supportsSystemDecorations();
}
+ // Update the display policy to make the screen fully turned on so animation is allowed
+ displayPolicy.screenTurnedOn(null /* screenOnListener */);
+ displayPolicy.finishKeyguardDrawn();
+ displayPolicy.finishWindowsDrawn();
+ displayPolicy.finishScreenTurningOn();
if (mStatusBarHeight > 0) {
doReturn(true).when(displayPolicy).hasStatusBar();
doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index f366f57..caaf4e4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -54,7 +54,6 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import android.window.ITransitionPlayer;
import androidx.test.filters.SmallTest;
@@ -313,11 +312,7 @@
wallpaperWindow.setHasSurface(true);
// Set-up mock shell transitions
- final IBinder mockBinder = mock(IBinder.class);
- final ITransitionPlayer mockPlayer = mock(ITransitionPlayer.class);
- doReturn(mockBinder).when(mockPlayer).asBinder();
- mWm.mAtmService.getTransitionController().registerTransitionPlayer(mockPlayer,
- null /* appThread */);
+ registerTestTransitionPlayer();
Transition transit =
mWm.mAtmService.getTransitionController().createTransition(TRANSIT_OPEN);
@@ -338,10 +333,21 @@
assertFalse(token.isVisibleRequested());
assertTrue(token.isVisible());
- transit.onTransactionReady(transit.getSyncId(), mock(SurfaceControl.Transaction.class));
- transit.finishTransition();
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ token.finishSync(t, false /* cancel */);
+ transit.onTransactionReady(transit.getSyncId(), t);
+ dc.mTransitionController.finishTransition(transit);
assertFalse(wallpaperWindow.isVisible());
assertFalse(token.isVisible());
+
+ // Assume wallpaper was visible. When transaction is ready without wallpaper target,
+ // wallpaper should be requested to be invisible.
+ token.setVisibility(true);
+ transit = dc.mTransitionController.createTransition(TRANSIT_CLOSE);
+ dc.mTransitionController.collect(token);
+ transit.onTransactionReady(transit.getSyncId(), t);
+ assertFalse(token.isVisibleRequested());
+ assertTrue(token.isVisible());
}
private WindowState createWallpaperTargetWindow(DisplayContent dc) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index e5eba57..646647f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -17,22 +17,35 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_OFF;
+import static android.view.Display.STATE_ON;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.window.WindowProvider.KEY_IS_WINDOW_PROVIDER_SERVICE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.app.IWindowToken;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
@@ -55,12 +68,15 @@
private static final int ANOTHER_UID = 1000;
private final IBinder mClientToken = new Binder();
- private WindowContainer mContainer;
+ private WindowContainer<?> mContainer;
@Before
public void setUp() {
mController = new WindowContextListenerController();
mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+ // Make display on to verify configuration propagation.
+ mDefaultDisplay.getDisplayInfo().state = STATE_ON;
+ mDisplayContent.getDisplayInfo().state = STATE_ON;
}
@Test
@@ -76,7 +92,7 @@
assertEquals(2, mController.mListeners.size());
- final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDefaultDisplay);
mController.registerWindowContainerListener(mClientToken, container, -1,
TYPE_APPLICATION_OVERLAY, null /* options */);
@@ -89,6 +105,7 @@
assertEquals(container, listener.getWindowContainer());
}
+ @UseTestDisplay
@Test
public void testRegisterWindowContextListenerClientConfigPropagation() {
final TestWindowTokenClient clientToken = new TestWindowTokenClient();
@@ -107,7 +124,7 @@
assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId);
// Update the WindowContainer.
- final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDefaultDisplay);
final Configuration config2 = container.getConfiguration();
final Rect bounds2 = new Rect(0, 0, 20, 20);
@@ -174,7 +191,7 @@
.setDisplayContent(mDefaultDisplay)
.setFromClientToken(true)
.build();
- final DisplayArea da = windowContextCreatedToken.getDisplayArea();
+ final DisplayArea<?> da = windowContextCreatedToken.getDisplayArea();
mController.registerWindowContainerListener(mClientToken, windowContextCreatedToken,
TEST_UID, TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
@@ -192,11 +209,12 @@
// Let the Display to be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
- Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
+ doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
// Create a DisplayContent with dual RootDisplayArea
DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent =
new DualDisplayAreaGroupPolicyTest.DualDisplayContent
.Builder(mAtm, 1000, 1000).build();
+ dualDisplayContent.getDisplayInfo().state = STATE_ON;
final DisplayArea.Tokens imeContainer = dualDisplayContent.getImeContainer();
// Put the ImeContainer to the first sub-RootDisplayArea
dualDisplayContent.mFirstRoot.placeImeContainer(imeContainer);
@@ -222,7 +240,62 @@
assertThat(mController.getContainer(mClientToken)).isEqualTo(imeContainer);
}
- private class TestWindowTokenClient extends IWindowToken.Stub {
+ @Test
+ public void testConfigUpdateForSuspendedWindowContext() {
+ final TestWindowTokenClient mockToken = new TestWindowTokenClient();
+ spyOn(mockToken);
+
+ mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;
+
+ final Configuration config1 = mContainer.getConfiguration();
+ final Rect bounds1 = new Rect(0, 0, 10, 10);
+ config1.windowConfiguration.setBounds(bounds1);
+ config1.densityDpi = 100;
+ mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+ mController.registerWindowContainerListener(mockToken, mContainer, -1,
+ TYPE_APPLICATION_OVERLAY, null /* options */);
+
+ verify(mockToken, never()).onConfigurationChanged(any(), anyInt());
+
+ // Turn on the display and verify if the client receive the callback
+ Display display = mContainer.getDisplayContent().getDisplay();
+ spyOn(display);
+ Mockito.doAnswer(invocation -> {
+ final DisplayInfo info = mContainer.getDisplayContent().getDisplayInfo();
+ info.state = STATE_ON;
+ ((DisplayInfo) invocation.getArgument(0)).copyFrom(info);
+ return null;
+ }).when(display).getDisplayInfo(any(DisplayInfo.class));
+
+ mContainer.getDisplayContent().onDisplayChanged();
+
+ assertThat(mockToken.mConfiguration).isEqualTo(config1);
+ assertThat(mockToken.mDisplayId).isEqualTo(mContainer.getDisplayContent().getDisplayId());
+ }
+
+ @Test
+ public void testReportConfigUpdateForSuspendedWindowProviderService() {
+ final TestWindowTokenClient clientToken = new TestWindowTokenClient();
+ final Bundle options = new Bundle();
+ options.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true);
+
+ mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;
+
+ final Configuration config1 = mContainer.getConfiguration();
+ final Rect bounds1 = new Rect(0, 0, 10, 10);
+ config1.windowConfiguration.setBounds(bounds1);
+ config1.densityDpi = 100;
+ mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+ mController.registerWindowContainerListener(clientToken, mContainer, -1,
+ TYPE_APPLICATION_OVERLAY, options);
+
+ assertThat(clientToken.mConfiguration).isEqualTo(config1);
+ assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId);
+ }
+
+ private static class TestWindowTokenClient extends IWindowToken.Stub {
private Configuration mConfiguration;
private int mDisplayId;
private boolean mRemoved;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 996b4b2..92fd682 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -198,6 +198,13 @@
SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
mDefaultDisplay = mWm.mRoot.getDefaultDisplay();
+ // Update the display policy to make the screen fully turned on so animation is allowed
+ final DisplayPolicy displayPolicy = mDefaultDisplay.getDisplayPolicy();
+ displayPolicy.screenTurnedOn(null /* screenOnListener */);
+ displayPolicy.finishKeyguardDrawn();
+ displayPolicy.finishWindowsDrawn();
+ displayPolicy.finishScreenTurningOn();
+
mTransaction = mSystemServicesTestRule.mTransaction;
mMockSession = mock(Session.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 722aee7..e5dc557 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -500,8 +500,6 @@
mDisplayContent.assignChildLayers(mTransaction);
assertWindowHigher(splitWindow1, belowTaskWindow);
- assertWindowHigher(splitWindow1, belowTaskWindow);
- assertWindowHigher(splitWindow2, belowTaskWindow);
assertWindowHigher(splitWindow2, belowTaskWindow);
assertWindowHigher(mDockedDividerWindow, splitWindow1);
assertWindowHigher(mDockedDividerWindow, splitWindow2);
@@ -509,6 +507,39 @@
assertWindowHigher(pinnedWindow, aboveTaskWindow);
}
+
+ @Test
+ public void testDockedDividerPosition_noAboveTask() {
+ final Task pinnedTask =
+ createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ final WindowState pinnedWindow =
+ createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow");
+
+ final Task belowTask =
+ createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState belowTaskWindow =
+ createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow");
+
+ final Task splitScreenTask1 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final WindowState splitWindow1 =
+ createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1");
+ final Task splitScreenTask2 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final WindowState splitWindow2 =
+ createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
+ splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2);
+ splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1);
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowHigher(splitWindow1, belowTaskWindow);
+ assertWindowHigher(splitWindow2, belowTaskWindow);
+ assertWindowHigher(mDockedDividerWindow, splitWindow1);
+ assertWindowHigher(mDockedDividerWindow, splitWindow2);
+ assertWindowHigher(pinnedWindow, mDockedDividerWindow);
+ }
+
@Test
public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() {
// create RecentsAnimationController
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index f4f06fd..96c78bc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -191,7 +191,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiver(mBroadcastReceiver, filter, null, handler,
- Context.RECEIVER_NOT_EXPORTED);
+ Context.RECEIVER_EXPORTED);
}
public boolean showSessionLocked(Bundle args, int flags,
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index abbce1c..98f619f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1687,7 +1687,11 @@
*
* @param accountHandle The handle for the account retrieve a number for.
* @return A string representation of the line 1 phone number.
+ * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
+ * Telephony Subscription ID that can be retrieved with the {@code accountHandle}
+ * from {@link TelephonyManager#getSubscriptionId(PhoneAccountHandle)}.
*/
+ @Deprecated
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
diff --git a/telephony/common/Android.bp b/telephony/common/Android.bp
index 1cacc03..b0a812b 100644
--- a/telephony/common/Android.bp
+++ b/telephony/common/Android.bp
@@ -21,7 +21,10 @@
filegroup {
name: "framework-mms-shared-srcs",
- visibility: ["//packages/apps/Bluetooth"],
+ visibility: [
+ "//packages/apps/Bluetooth",
+ "//packages/modules/Bluetooth/android/app",
+ ],
srcs: [
"com/google/android/mms/**/*.java",
],
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 7d857a2..fabe612 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -856,9 +856,10 @@
mExecutor.execute(new Runnable() {
@Override
public void run() {
+ // TODO(b/207392528: use portIndex API once implemented)
int result =
- EuiccService.this.onSwitchToSubscriptionWithPort(
- slotId, portIndex, iccid, forceDeactivateSim);
+ EuiccService.this.onSwitchToSubscription(
+ slotId, iccid, forceDeactivateSim);
try {
callback.onComplete(result);
} catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 23cf511..e88106c 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -1,6 +1,8 @@
package android.telephony;
import android.annotation.IntDef;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
import android.telecom.Connection;
import android.telephony.data.ApnSetting;
@@ -664,4 +666,59 @@
TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE,
TelephonyManager.THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR})
public @interface ThermalMitigationResult {}
+
+ /**
+ * Per Android API guideline 8.15, annotation can't be public APIs. So duplicate
+ * android.net.NetworkCapabilities.NetCapability here. Must update here when new capabilities
+ * are added in {@link NetworkCapabilities}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "NET_CAPABILITY_" }, value = {
+ NetworkCapabilities.NET_CAPABILITY_MMS,
+ NetworkCapabilities.NET_CAPABILITY_SUPL,
+ NetworkCapabilities.NET_CAPABILITY_DUN,
+ NetworkCapabilities.NET_CAPABILITY_FOTA,
+ NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_CBS,
+ NetworkCapabilities.NET_CAPABILITY_WIFI_P2P,
+ NetworkCapabilities.NET_CAPABILITY_IA,
+ NetworkCapabilities.NET_CAPABILITY_RCS,
+ NetworkCapabilities.NET_CAPABILITY_XCAP,
+ NetworkCapabilities.NET_CAPABILITY_EIMS,
+ NetworkCapabilities.NET_CAPABILITY_NOT_METERED,
+ NetworkCapabilities.NET_CAPABILITY_INTERNET,
+ NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED,
+ NetworkCapabilities.NET_CAPABILITY_TRUSTED,
+ NetworkCapabilities.NET_CAPABILITY_NOT_VPN,
+ NetworkCapabilities.NET_CAPABILITY_VALIDATED,
+ NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL,
+ NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
+ NetworkCapabilities.NET_CAPABILITY_FOREGROUND,
+ NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED,
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED,
+ NetworkCapabilities.NET_CAPABILITY_OEM_PAID,
+ NetworkCapabilities.NET_CAPABILITY_MCX,
+ NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+ NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+ NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE,
+ NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED,
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE,
+ NetworkCapabilities.NET_CAPABILITY_VSIM,
+ NetworkCapabilities.NET_CAPABILITY_BIP,
+ NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT,
+ })
+ public @interface NetCapability { }
+
+ /**
+ * Per Android API guideline 8.15, annotation can't be public APIs. So duplicate
+ * android.net.NetworkAgent.ValidationStatus here. Must update here when new validation status
+ * are added in {@link NetworkAgent}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "VALIDATION_STATUS_" }, value = {
+ NetworkAgent.VALIDATION_STATUS_VALID,
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID
+ })
+ public @interface ValidationStatus {}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3f0f50c..6f92c31 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5116,13 +5116,11 @@
public static final String KEY_APN_PRIORITY_STRING_ARRAY = "apn_priority_string_array";
/**
- * Network capability priority for determine the satisfy order in telephony. This is used when
- * the network only allows single PDN. The priority is from the lowest 0 to the highest 100.
- * The long-lived network request usually has the lowest priority. This allows other short-lived
- * requests like MMS requests to be established. Emergency request always has the highest
- * priority.
+ * Network capability priority for determine the satisfy order in telephony. The priority is
+ * from the lowest 0 to the highest 100. The long-lived network shall have the lowest priority.
+ * This allows other short-lived requests like MMS requests to be established. Emergency request
+ * always has the highest priority.
*
- * // TODO: Remove KEY_APN_PRIORITY_STRING_ARRAY
* @hide
*/
public static final String KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY =
@@ -5132,17 +5130,17 @@
* Defines the rules for data retry.
*
* The syntax of the retry rule:
- * 1. Retry based on {@link NetworkCapabilities}
- * "capabilities=[netCaps1|netCaps2|...], [retry_interval=x], [backoff=[true|false]],
- * [maximum_retries=y]"
+ * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities
+ * are supported.
+ * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
*
* 2. Retry based on {@link DataFailCause}
- * "fail_causes=[cause1|cause2|cause3|...], [retry_interval=x], [backoff=[true|false]],
- * [maximum_retries=y]"
+ * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
*
- * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}
+ * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}. Note that only
+ * APN-type network capabilities are supported.
* "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...],
- * [retry_interval=x], [backoff=[true|false]], [maximum_retries=y]"
+ * [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
*
* For example,
* "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached
@@ -5151,7 +5149,12 @@
*
* "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|2254
* , maximum_retries=0" means for those fail causes, never retry with timers. Note that
- * when environment changes, retry can still happens.
+ * when environment changes, retry can still happen.
+ *
+ * "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
+ * "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000"
+ * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
+ * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
*
* // TODO: remove KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS
* @hide
@@ -5941,6 +5944,9 @@
"enterprise:0", "default:1", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2",
"ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
});
+
+ // Do not modify the priority unless you know what you are doing. This will have significant
+ // impacts on the order of data network setup.
sDefaults.putStringArray(
KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY, new String[] {
"eims:90", "supl:80", "mms:70", "xcap:70", "cbs:50", "mcx:50", "fota:50",
@@ -5951,9 +5957,10 @@
"capabilities=eims, retry_interval=1000, maximum_retries=20",
"fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|"
+ "2254, maximum_retries=0", // No retry for those causes
- "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2000, "
- + "backoff=true, maximum_retries=13",
- "capabilities=mms|supl|cbs, retry_interval=2000"
+ "capabilities=mms|supl|cbs, retry_interval=2000",
+ "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
+ + "5000|10000|15000|20000|40000|60000|120000|240000|"
+ + "600000|1200000|1800000, maximum_retries=20"
});
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 88efe1f..56bf303 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1076,6 +1076,13 @@
*/
public static final int SERVICE_TEMPORARILY_UNAVAILABLE = 0x10009;
+ /**
+ * The request is not supported by the vendor.
+ *
+ * @hide
+ */
+ public static final int REQUEST_NOT_SUPPORTED = 0x1000A;
+
private static final Map<Integer, String> sFailCauseMap;
static {
sFailCauseMap = new HashMap<>();
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 2ff4ac5..9cb80f1 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -71,12 +71,7 @@
@Nullable List<SignalThresholdInfo> signalThresholdInfos,
boolean isReportingRequestedWhileIdle,
boolean isSystemThresholdReportingRequestedWhileIdle) {
- // System app (like Bluetooth) can specify the request to report system thresholds while
- // device is idle (with permission protection). In this case, the request doesn't need to
- // provide a non-empty list of SignalThresholdInfo which is only asked for public apps.
- if (!isSystemThresholdReportingRequestedWhileIdle) {
- validate(signalThresholdInfos);
- }
+ validate(signalThresholdInfos, isSystemThresholdReportingRequestedWhileIdle);
mSignalThresholdInfos = signalThresholdInfos;
mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle;
@@ -274,8 +269,12 @@
* Throw IAE if SignalThresholdInfo collection is null or empty,
* or the SignalMeasurementType for the same RAN in the collection is not unique.
*/
- private static void validate(Collection<SignalThresholdInfo> infos) {
- if (infos == null || infos.isEmpty()) {
+ private static void validate(Collection<SignalThresholdInfo> infos,
+ boolean isSystemThresholdReportingRequestedWhileIdle) {
+ // System app (like Bluetooth) can specify the request to report system thresholds while
+ // device is idle (with permission protection). In this case, the request doesn't need to
+ // provide a non-empty list of SignalThresholdInfo which is only asked for public apps.
+ if (infos == null || (infos.isEmpty() && !isSystemThresholdReportingRequestedWhileIdle)) {
throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty");
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index d6d6775..d11ad91 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -498,7 +498,10 @@
*
* @return the number of this subscription, or an empty string if one of these requirements is
* not met
+ * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
+ * {@link #getSubscriptionId() subscription ID}.
*/
+ @Deprecated
public String getNumber() {
return mNumber;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 04e7b7c..ae2facd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3465,15 +3465,39 @@
* @see #SIM_STATE_PRESENT
*
* @hide
+ * @deprecated instead use {@link #getSimCardState(int, int)}
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @Deprecated
public @SimState int getSimCardState(int physicalSlotIndex) {
- int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex));
+ int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex, DEFAULT_PORT_INDEX));
return getSimCardStateFromSimState(simState);
}
/**
+ * Returns a constant indicating the state of the device SIM card in a physical slot and
+ * port index.
+ *
+ * @param physicalSlotIndex physical slot index
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ *
+ * @see #SIM_STATE_UNKNOWN
+ * @see #SIM_STATE_ABSENT
+ * @see #SIM_STATE_CARD_IO_ERROR
+ * @see #SIM_STATE_CARD_RESTRICTED
+ * @see #SIM_STATE_PRESENT
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @SimState int getSimCardState(int physicalSlotIndex, int portIndex) {
+ int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex, portIndex));
+ return getSimCardStateFromSimState(simState);
+ }
+ /**
* Converts SIM state to SIM card state.
* @param simState
* @return SIM card state
@@ -3493,13 +3517,19 @@
/**
* Converts a physical slot index to logical slot index.
* @param physicalSlotIndex physical slot index
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
* @return logical slot index
*/
- private int getLogicalSlotIndex(int physicalSlotIndex) {
+ private int getLogicalSlotIndex(int physicalSlotIndex, int portIndex) {
UiccSlotInfo[] slotInfos = getUiccSlotsInfo();
if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length
&& slotInfos[physicalSlotIndex] != null) {
- return slotInfos[physicalSlotIndex].getLogicalSlotIdx();
+ for (UiccPortInfo portInfo : slotInfos[physicalSlotIndex].getPorts()) {
+ if (portInfo.getPortIndex() == portIndex) {
+ return portInfo.getLogicalSlotIndex();
+ }
+ }
}
return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
@@ -3539,12 +3569,42 @@
* @see #SIM_STATE_LOADED
*
* @hide
+ * @deprecated instead use {@link #getSimApplicationState(int, int)}
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @Deprecated
public @SimState int getSimApplicationState(int physicalSlotIndex) {
int simState =
- SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex));
+ SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex,
+ DEFAULT_PORT_INDEX));
+ return getSimApplicationStateFromSimState(simState);
+ }
+
+ /**
+ * Returns a constant indicating the state of the card applications on the device SIM card in
+ * a physical slot.
+ *
+ * @param physicalSlotIndex physical slot index
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ *
+ * @see #SIM_STATE_UNKNOWN
+ * @see #SIM_STATE_PIN_REQUIRED
+ * @see #SIM_STATE_PUK_REQUIRED
+ * @see #SIM_STATE_NETWORK_LOCKED
+ * @see #SIM_STATE_NOT_READY
+ * @see #SIM_STATE_PERM_DISABLED
+ * @see #SIM_STATE_LOADED
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @SimState int getSimApplicationState(int physicalSlotIndex, int portIndex) {
+ int simState =
+ SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex,
+ portIndex));
return getSimApplicationStateFromSimState(simState);
}
@@ -4143,18 +4203,21 @@
* should be {@link #getPhoneCount()} if success, otherwise return an empty map.
*
* @hide
+ * @deprecated use {@link #getSimSlotMapping()} instead.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@NonNull
+ @Deprecated
public Map<Integer, Integer> getLogicalToPhysicalSlotMapping() {
Map<Integer, Integer> slotMapping = new HashMap<>();
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- int[] slotMappingArray = telephony.getSlotsMapping(mContext.getOpPackageName());
- for (int i = 0; i < slotMappingArray.length; i++) {
- slotMapping.put(i, slotMappingArray[i]);
+ List<UiccSlotMapping> simSlotsMapping = telephony.getSlotsMapping(
+ mContext.getOpPackageName());
+ for (UiccSlotMapping slotMap : simSlotsMapping) {
+ slotMapping.put(slotMap.getLogicalSlotIndex(), slotMap.getPhysicalSlotIndex());
}
}
} catch (RemoteException e) {
@@ -4163,6 +4226,33 @@
return slotMapping;
}
+ /**
+ * Get the mapping from logical slots to physical sim slots and port indexes. Initially the
+ * logical slot index was mapped to physical slot index, but with support for multi-enabled
+ * profile(MEP) logical slot is now mapped to port index.
+ *
+ * @return a collection of {@link UiccSlotMapping} which indicates the mapping from logical
+ * slots to ports and physical slots.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @NonNull
+ public Collection<UiccSlotMapping> getSimSlotMapping() {
+ List<UiccSlotMapping> slotMap = new ArrayList<>();
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ slotMap = telephony.getSlotsMapping(mContext.getOpPackageName());
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ return slotMap;
+ }
//
//
// Subscriber Info
@@ -4791,7 +4881,10 @@
* for any API level.
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* for apps targeting SDK API level 29 and below.
+ *
+ * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead.
*/
+ @Deprecated
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
@@ -4864,7 +4957,9 @@
* @param alphaTag alpha-tagging of the dailing nubmer
* @param number The dialing number
* @return true if the operation was executed correctly.
+ * @deprecated use {@link SubscriptionManager#setCarrierPhoneNumber(int, String)} instead.
*/
+ @Deprecated
public boolean setLine1NumberForDisplay(String alphaTag, String number) {
return setLine1NumberForDisplay(getSubId(), alphaTag, number);
}
@@ -4885,6 +4980,10 @@
*/
public boolean setLine1NumberForDisplay(int subId, String alphaTag, String number) {
try {
+ // This API is deprecated; call the new API to allow smooth migartion.
+ // The new API doesn't accept null so convert null to empty string.
+ mSubscriptionManager.setCarrierPhoneNumber(subId, (number == null ? "" : number));
+
ITelephony telephony = getITelephony();
if (telephony != null)
return telephony.setLine1NumberForDisplayForSubscriber(subId, alphaTag, number);
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 7dfe450..74f9c87 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -156,9 +156,11 @@
@Nullable
@Deprecated
public String getIccId() {
- if (mIccIdAccessRestricted) {
- throw new UnsupportedOperationException("getIccId from UiccPortInfo");
- }
+ // Temporarily bypassing exception
+ // TODO: add exception once refactoring completed.
+ //if (mIccIdAccessRestricted) {
+ // throw new UnsupportedOperationException("getIccId from UiccPortInfo");
+ //}
//always return ICCID from first port.
return getPorts().stream().findFirst().get().getIccId();
}
@@ -258,7 +260,7 @@
+ ", mEid="
+ mEid
+ ", mIccId="
- + SubscriptionInfo.givePrintableIccid(mIccId)
+ + SubscriptionInfo.givePrintableIccid(getIccId())
+ ", mPhysicalSlotIndex="
+ mPhysicalSlotIndex
+ ", mIsRemovable="
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 2b1c8c8..a8668e7 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -159,9 +159,11 @@
*/
@Deprecated
public boolean getIsActive() {
- if (mLogicalSlotAccessRestricted) {
- throw new UnsupportedOperationException("get port status from UiccPortInfo");
- }
+ // Temporarily bypassing exception
+ // TODO: add exception once refactoring completed.
+ //if (mLogicalSlotAccessRestricted) {
+ // throw new UnsupportedOperationException("get port status from UiccPortInfo");
+ //}
//always return status from first port.
return getPorts().stream().findFirst().get().isActive();
}
@@ -196,9 +198,11 @@
*/
@Deprecated
public int getLogicalSlotIdx() {
- if (mLogicalSlotAccessRestricted) {
- throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
- }
+ // Temporarily bypassing exception
+ // TODO: add exception once refactoring completed.
+ //if (mLogicalSlotAccessRestricted) {
+ // throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
+ //}
//always return logical slot index from first port.
//portList always have at least one element.
return getPorts().stream().findFirst().get().getLogicalSlotIndex();
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index 93903d2..c1d16a9 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -18,13 +18,16 @@
import static android.telephony.data.ApnSetting.ProtocolType;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.net.NetworkCapabilities;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.NetCapability;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.NetworkTypeBitMask;
import android.telephony.data.ApnSetting.AuthType;
@@ -66,7 +69,13 @@
private final @Nullable TrafficDescriptor mTrafficDescriptor;
- private final boolean mPreferred;
+ private boolean mPreferred;
+
+ /**
+ * The last timestamp of this data profile being used for data network setup. Never add this
+ * to {@link #equals(Object)} and {@link #hashCode()}.
+ */
+ private @ElapsedRealtimeLong long mSetupTimestamp;
private DataProfile(@NonNull Builder builder) {
mApnSetting = builder.mApnSetting;
@@ -99,6 +108,7 @@
mApnSetting = source.readParcelable(ApnSetting.class.getClassLoader());
mTrafficDescriptor = source.readParcelable(TrafficDescriptor.class.getClassLoader());
mPreferred = source.readBoolean();
+ mSetupTimestamp = source.readLong();
}
/**
@@ -291,6 +301,16 @@
}
/**
+ * Set the preferred flag for the data profile.
+ *
+ * @param preferred {@code true} if this data profile is preferred for internet.
+ * @hide
+ */
+ public void setPreferred(boolean preferred) {
+ mPreferred = preferred;
+ }
+
+ /**
* @return {@code true} if this data profile was used to bring up the last default
* (i.e internet) data connection successfully, or the one chosen by the user in Settings'
* APN editor. For one carrier there can be only one profiled preferred.
@@ -315,6 +335,94 @@
return mTrafficDescriptor;
}
+ /**
+ * Check if this data profile can satisfy certain network capabilities
+ *
+ * @param networkCapabilities The network capabilities. Note that the non-APN-type capabilities
+ * will be ignored.
+ *
+ * @return {@code true} if this data profile can satisfy the given network capabilities.
+ * @hide
+ */
+ public boolean canSatisfy(@NonNull @NetCapability int[] networkCapabilities) {
+ if (mApnSetting != null) {
+ for (int netCap : networkCapabilities) {
+ if (!canSatisfy(netCap)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if this data profile can satisfy a certain network capability.
+ *
+ * @param networkCapability The network capability. Note that the non-APN-type capability
+ * will always be satisfied.
+ * @return {@code true} if this data profile can satisfy the given network capability.
+ * @hide
+ */
+ public boolean canSatisfy(@NetCapability int networkCapability) {
+ return mApnSetting != null && mApnSetting.canHandleType(
+ networkCapabilityToApnType(networkCapability));
+ }
+
+ /**
+ * Convert network capability into APN type.
+ *
+ * @param networkCapability Network capability.
+ * @return APN type.
+ * @hide
+ */
+ private static @ApnType int networkCapabilityToApnType(@NetCapability int networkCapability) {
+ switch (networkCapability) {
+ case NetworkCapabilities.NET_CAPABILITY_MMS:
+ return ApnSetting.TYPE_MMS;
+ case NetworkCapabilities.NET_CAPABILITY_SUPL:
+ return ApnSetting.TYPE_SUPL;
+ case NetworkCapabilities.NET_CAPABILITY_DUN:
+ return ApnSetting.TYPE_DUN;
+ case NetworkCapabilities.NET_CAPABILITY_FOTA:
+ return ApnSetting.TYPE_FOTA;
+ case NetworkCapabilities.NET_CAPABILITY_IMS:
+ return ApnSetting.TYPE_IMS;
+ case NetworkCapabilities.NET_CAPABILITY_CBS:
+ return ApnSetting.TYPE_CBS;
+ case NetworkCapabilities.NET_CAPABILITY_XCAP:
+ return ApnSetting.TYPE_XCAP;
+ case NetworkCapabilities.NET_CAPABILITY_EIMS:
+ return ApnSetting.TYPE_EMERGENCY;
+ case NetworkCapabilities.NET_CAPABILITY_INTERNET:
+ return ApnSetting.TYPE_DEFAULT;
+ case NetworkCapabilities.NET_CAPABILITY_MCX:
+ return ApnSetting.TYPE_MCX;
+ case NetworkCapabilities.NET_CAPABILITY_IA:
+ return ApnSetting.TYPE_IA;
+ default:
+ return ApnSetting.TYPE_NONE;
+ }
+ }
+
+ /**
+ * Set the timestamp of this data profile being used for data network setup.
+ *
+ * @hide
+ */
+ public void setLastSetupTimestamp(@ElapsedRealtimeLong long timestamp) {
+ mSetupTimestamp = timestamp;
+ }
+
+ /**
+ * @return the timestamp of this data profile being used for data network setup.
+ *
+ * @hide
+ */
+ public @ElapsedRealtimeLong long getLastSetupTimestamp() {
+ return mSetupTimestamp;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -323,8 +431,8 @@
@NonNull
@Override
public String toString() {
- return "DataProfile=" + mApnSetting + ", " + mTrafficDescriptor + ", preferred="
- + mPreferred;
+ return "[DataProfile=" + mApnSetting + ", " + mTrafficDescriptor + ", preferred="
+ + mPreferred + "]";
}
@Override
@@ -333,6 +441,7 @@
dest.writeParcelable(mApnSetting, flags);
dest.writeParcelable(mTrafficDescriptor, flags);
dest.writeBoolean(mPreferred);
+ dest.writeLong(mSetupTimestamp);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataProfile> CREATOR =
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
index c00c741..1b10404 100644
--- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -117,6 +117,7 @@
})
public @interface DeregisteringReason {}
+ private ArraySet<String> mRegisteringTags = new ArraySet<>();
private ArraySet<String> mRegisteredTags = new ArraySet<>();
private final ArraySet<FeatureTagState> mDeregisteringTags = new ArraySet<>();
private final ArraySet<FeatureTagState> mDeregisteredTags = new ArraySet<>();
@@ -134,6 +135,20 @@
}
/**
+ * Add the set of feature tags that are associated with this SipDelegate and
+ * the IMS stack is actively trying to register on the carrier network.
+ *
+ * The feature tags will either move to the registered or deregistered state
+ * depending on the result of the registration.
+ * @param featureTags The IMS media feature tags that are in the progress of registering.
+ * @return The in-progress Builder instance for RegistrationState. ]
+ */
+ public @NonNull Builder addRegisteringFeatureTags(@NonNull Set<String> featureTags) {
+ mState.mRegisteringTags.addAll(featureTags);
+ return this;
+ }
+
+ /**
* Add a feature tag that is currently included in the current network IMS Registration.
* @param featureTag The IMS media feature tag included in the current IMS registration.
* @return The in-progress Builder instance for RegistrationState.
@@ -209,6 +224,17 @@
mRegisteredTags = (ArraySet<String>) source.readArraySet(null);
readStateFromParcel(source, mDeregisteringTags);
readStateFromParcel(source, mDeregisteredTags);
+ mRegisteringTags = (ArraySet<String>) source.readArraySet(null);
+ }
+
+ /**
+ * Get the feature tags that are associated with this SipDelegate that the IMS stack is actively
+ * trying to register on the carrier network.
+ * @return A Set of feature tags associated with this SipDelegate that the IMS service is
+ * currently trying to register on the carrier network.
+ */
+ public @NonNull Set<String> getRegisteringFeatureTags() {
+ return new ArraySet<>(mRegisteringTags);
}
/**
@@ -286,6 +312,7 @@
dest.writeArraySet(mRegisteredTags);
writeStateToParcel(dest, mDeregisteringTags);
writeStateToParcel(dest, mDeregisteredTags);
+ dest.writeArraySet(mRegisteringTags);
}
private void writeStateToParcel(Parcel dest, Set<FeatureTagState> state) {
@@ -311,19 +338,22 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DelegateRegistrationState that = (DelegateRegistrationState) o;
- return mRegisteredTags.equals(that.mRegisteredTags)
+ return mRegisteringTags.equals(that.mRegisteringTags)
+ && mRegisteredTags.equals(that.mRegisteredTags)
&& mDeregisteringTags.equals(that.mDeregisteringTags)
&& mDeregisteredTags.equals(that.mDeregisteredTags);
}
@Override
public int hashCode() {
- return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags);
+ return Objects.hash(mRegisteringTags, mRegisteredTags,
+ mDeregisteringTags, mDeregisteredTags);
}
@Override
public String toString() {
return "DelegateRegistrationState{ registered={" + mRegisteredTags
+ + "}, registering={" + mRegisteringTags
+ "}, deregistering={" + mDeregisteringTags + "}, deregistered={"
+ mDeregisteredTags + "}}";
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index dbc6cb6..6d094cb 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2134,9 +2134,9 @@
String callingFeatureId);
/**
- * Get the mapping from logical slots to physical slots.
+ * Get the mapping from logical slots to port index.
*/
- int[] getSlotsMapping(String callingPackage);
+ List<UiccSlotMapping> getSlotsMapping(String callingPackage);
/**
* Get the IRadio HAL Version encoded as 100 * MAJOR_VERSION + MINOR_VERSION or -1 if unknown
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 0bb6198..22320fd 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -27,9 +27,8 @@
java_sdk_library {
name: "android.test.mock",
-
- srcs: [
- ":android-test-mock-sources",
+ srcs: [":android-test-mock-sources"],
+ api_srcs: [
// Note: Below are NOT APIs of this library. We only take APIs under
// the android.test.mock package. They however provide private APIs that
// android.test.mock APIs references to. We need to have the classes in
@@ -44,15 +43,9 @@
"app-compat-annotations",
"unsupportedappusage",
],
-
api_packages: [
"android.test.mock",
],
- // Only include android.test.mock.* classes. Jarjar rules below removes
- // classes in other packages like android.content. In order to keep the
- // list up-to-date, permitted_packages ensures that the library contains
- // clases under android.test.mock after the jarjar rules are applied.
- jarjar_rules: "jarjar-rules.txt",
permitted_packages: [
"android.test.mock",
],
diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt
deleted file mode 100644
index 4420a44..0000000
--- a/test-mock/jarjar-rules.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-zap android.accounts.**
-zap android.app.**
-zap android.content.**
-zap android.database.**
-zap android.os.**
-zap android.util.**
-zap android.view.**
diff --git a/tests/AttestationVerificationTest/Android.bp b/tests/AttestationVerificationTest/Android.bp
new file mode 100644
index 0000000..a4741eed
--- /dev/null
+++ b/tests/AttestationVerificationTest/Android.bp
@@ -0,0 +1,44 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "AttestationVerificationTest",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ defaults: ["cts_defaults"],
+ manifest: "AndroidManifest.xml",
+ test_config: "AndroidTest.xml",
+ platform_apis: true,
+ certificate: "platform",
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "platform-test-annotations",
+ ],
+}
diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml
new file mode 100755
index 0000000..c42bde9
--- /dev/null
+++ b/tests/AttestationVerificationTest/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.attestationverification">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+ <uses-permission android:name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".SystemAttestationVerificationTest$TestActivity" />
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.attestationverification">
+ </instrumentation>
+</manifest>
diff --git a/tests/AttestationVerificationTest/AndroidTest.xml b/tests/AttestationVerificationTest/AndroidTest.xml
new file mode 100644
index 0000000..1325760
--- /dev/null
+++ b/tests/AttestationVerificationTest/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+<configuration description="Platform tests for Attestation Verification Framework">
+ <option name="test-tag" value="AttestationVerificationTest" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="AttestationVerificationTest.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.security.attestationverification" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
new file mode 100644
index 0000000..48bfd6f
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
@@ -0,0 +1,90 @@
+package android.security.attestationverification
+
+import android.os.Bundle
+import android.app.Activity
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import com.google.common.truth.Truth.assertThat
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
+import java.lang.IllegalArgumentException
+import java.time.Duration
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+
+/** Test for system-defined attestation verifiers. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SystemAttestationVerificationTest {
+
+ @get:Rule
+ val rule = ActivityScenarioRule(TestActivity::class.java)
+
+ private lateinit var activity: Activity
+ private lateinit var avm: AttestationVerificationManager
+
+ @Before
+ fun setup() {
+ rule.getScenario().onActivity {
+ avm = it.getSystemService(AttestationVerificationManager::class.java)
+ activity = it
+ }
+ }
+
+ @Test
+ fun verifyAttestation_returnsUnknown() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+ activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
+ }
+
+ @Test
+ fun verifyToken_returnsUnknown() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+ activity.mainExecutor) { _, token ->
+ val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
+ }
+
+ @Test
+ fun verifyToken_tooBigMaxAgeThrows() {
+ val future = CompletableFuture<VerificationToken>()
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+ activity.mainExecutor) { _, token ->
+ future.complete(token)
+ }
+
+ assertThrows(IllegalArgumentException::class.java) {
+ avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), future.getSoon(),
+ Duration.ofSeconds(3601))
+ }
+ }
+
+ private fun <T> CompletableFuture<T>.getSoon(): T {
+ return this.get(1, TimeUnit.SECONDS)
+ }
+
+ class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 3c9ba20..ca735031 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.close
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
@@ -196,13 +195,13 @@
testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
}
- @Postsubmit
+ @FlakyTest
@Test
fun runPresubmitAssertion() {
flickerRule.checkPresubmitAssertions()
}
- @Postsubmit
+ @FlakyTest
@Test
fun runPostsubmitAssertion() {
flickerRule.checkPostsubmitAssertions()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index b1bdb31..39d2518 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -17,10 +17,10 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -105,7 +105,7 @@
}
}
- @FlakyTest(bugId = 190189685)
+ @Postsubmit
@Test
fun imeAppWindowBecomesInvisible() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index ac90752..61fe02e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -17,10 +17,10 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -106,7 +106,7 @@
@Test
fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
- @FlakyTest
+ @Postsubmit
@Test
fun imeAppWindowBecomesInvisible() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index cc5d9d2..1c14916 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -17,10 +17,10 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -144,7 +144,7 @@
}
}
- @FlakyTest
+ @Postsubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 429841b..61fe07a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -110,7 +110,7 @@
* Checks that the app layer doesn't exist at the start of the transition, that it is
* created (invisible) and becomes visible during the transition
*/
- @FlakyTest
+ @Postsubmit
@Test
fun appLayerBecomesVisible() {
testSpec.assertLayers {
@@ -169,7 +169,7 @@
}
/** {@inheritDoc} */
- @FlakyTest
+ @Postsubmit
@Test
override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
@@ -211,7 +211,7 @@
}
/** {@inheritDoc} */
- @FlakyTest
+ @Postsubmit
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
@@ -227,7 +227,7 @@
super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
- @Postsubmit
+ @FlakyTest
@Test
override fun entireScreenCovered() = super.entireScreenCovered()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 5b0372d..0a64939 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -18,6 +18,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -133,7 +134,7 @@
/**
* Checks that the transition starts with [testApp2] being the top window.
*/
- @Postsubmit
+ @Presubmit
@Test
fun startsWithApp2WindowBeingOnTop() {
testSpec.assertWmStart {
@@ -284,7 +285,7 @@
/**
* Checks that the navbar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
@@ -293,21 +294,21 @@
*
* NOTE: This doesn't check that the navbar is visible or not.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
/**
* Checks that the status bar window is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
/**
* Checks that the status bar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 99bc115..5b63376 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -18,6 +18,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -298,7 +299,7 @@
/**
* Checks that the navbar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
@@ -307,21 +308,21 @@
*
* NOTE: This doesn't check that the navbar is visible or not.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
/**
* Checks that the status bar window is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
/**
* Checks that the status bar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index a97a48e..c18798f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.rotation
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -97,13 +96,13 @@
}
}
- @Postsubmit
+ @FlakyTest
@Test
fun runPresubmitAssertion() {
flickerRule.checkPresubmitAssertions()
}
- @Postsubmit
+ @FlakyTest
@Test
fun runPostsubmitAssertion() {
flickerRule.checkPostsubmitAssertions()
@@ -115,11 +114,16 @@
flickerRule.checkFlakyAssertions()
}
- /** {@inheritDoc} */
+ /**
+ * Windows maybe recreated when rotated. Checks that the focus does not change or if it does,
+ * focus returns to [testApp]
+ */
@FlakyTest(bugId = 190185577)
@Test
- override fun focusDoesNotChange() {
- super.focusDoesNotChange()
+ fun focusChanges() {
+ testSpec.assertEventLog {
+ this.focusChanges(testApp.`package`)
+ }
}
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index ce2347d..d1bdeed 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -129,17 +129,6 @@
open fun entireScreenCovered() = testSpec.entireScreenCovered()
/**
- * Checks that the focus doesn't change during animation
- */
- @Presubmit
- @Test
- open fun focusDoesNotChange() {
- testSpec.assertEventLog {
- this.focusDoesNotChange()
- }
- }
-
- /**
* Checks that [testApp] layer covers the entire screen at the start of the transition
*/
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 3ca60e3..e44bee6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -146,15 +146,6 @@
}
}
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun focusDoesNotChange() {
- // This test doesn't work in shell transitions because of b/206101151
- assumeFalse(isShellTransitionsEnabled)
- super.focusDoesNotChange()
- }
-
/**
* Checks that [testApp] layer covers the entire screen during the whole transition
*/
@@ -196,6 +187,19 @@
}
}
+ /**
+ * Checks that the focus doesn't change during animation
+ */
+ @Presubmit
+ @Test
+ fun focusDoesNotChange() {
+ // This test doesn't work in shell transitions because of b/206101151
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
+ }
+
/** {@inheritDoc} */
@FlakyTest
@Test
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
index 7ea2a62d..d4bc2a6 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
@@ -42,7 +42,7 @@
swView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
frame.addView(swView);
final RectsView hwBothView = new RectsView(this, 850, Color.GREEN);
- // Don't actually need to render to a hw layer, but it's a good sanity-check that
+ // Don't actually need to render to a hw layer, but it's a good check that
// we're rendering to/from layers correctly
hwBothView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
frame.addView(hwBothView);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
index 7173a85..584ab59 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
@@ -42,7 +42,7 @@
swView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
frame.addView(swView);
final LinesView hwBothView = new LinesView(this, 850, Color.GREEN);
- // Don't actually need to render to a hw layer, but it's a good sanity-check that
+ // Don't actually need to render to a hw layer, but it's a good check that
// we're rendering to/from layers correctly
hwBothView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
frame.addView(hwBothView);
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index eacf5b2..6e65350 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -18,6 +18,7 @@
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
+ "services.core.unboosted",
"truth-prebuilt",
"ub-uiautomator",
],
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
index 014efc2..37b67f4 100644
--- a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -17,16 +17,11 @@
package com.android.test.input
import android.os.HandlerThread
-import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS
import android.os.Looper
import android.view.InputChannel
import android.view.InputEvent
import android.view.InputEventReceiver
-import android.view.InputEventSender
import android.view.KeyEvent
-import android.view.MotionEvent
-import java.util.concurrent.LinkedBlockingQueue
-import java.util.concurrent.TimeUnit
import org.junit.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -46,54 +41,19 @@
assertEquals(expected.displayId, received.displayId)
}
-private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T {
- try {
- return queue.poll(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS)
- } catch (e: InterruptedException) {
- throw RuntimeException("Unexpectedly interrupted while waiting for event")
- }
+private fun getTestKeyEvent(): KeyEvent {
+ return KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_A, 0 /*repeat*/)
}
-class TestInputEventReceiver(channel: InputChannel, looper: Looper) :
+private class CrashingInputEventReceiver(channel: InputChannel, looper: Looper) :
InputEventReceiver(channel, looper) {
- private val mInputEvents = LinkedBlockingQueue<InputEvent>()
-
override fun onInputEvent(event: InputEvent) {
- when (event) {
- is KeyEvent -> mInputEvents.put(KeyEvent.obtain(event))
- is MotionEvent -> mInputEvents.put(MotionEvent.obtain(event))
- else -> throw Exception("Received $event is neither a key nor a motion")
+ try {
+ throw IllegalArgumentException("This receiver crashes when it receives input event")
+ } finally {
+ finishInputEvent(event, true /*handled*/)
}
- finishInputEvent(event, true /*handled*/)
- }
-
- fun getInputEvent(): InputEvent {
- return getEvent(mInputEvents)
- }
-}
-
-class TestInputEventSender(channel: InputChannel, looper: Looper) :
- InputEventSender(channel, looper) {
- data class FinishedSignal(val seq: Int, val handled: Boolean)
- data class Timeline(val inputEventId: Int, val gpuCompletedTime: Long, val presentTime: Long)
-
- private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>()
- private val mTimelines = LinkedBlockingQueue<Timeline>()
-
- override fun onInputEventFinished(seq: Int, handled: Boolean) {
- mFinishedSignals.put(FinishedSignal(seq, handled))
- }
-
- override fun onTimelineReported(inputEventId: Int, gpuCompletedTime: Long, presentTime: Long) {
- mTimelines.put(Timeline(inputEventId, gpuCompletedTime, presentTime))
- }
-
- fun getFinishedSignal(): FinishedSignal {
- return getEvent(mFinishedSignals)
- }
-
- fun getTimeline(): Timeline {
- return getEvent(mTimelines)
}
}
@@ -102,8 +62,8 @@
private const val TAG = "InputEventSenderAndReceiverTest"
}
private val mHandlerThread = HandlerThread("Process input events")
- private lateinit var mReceiver: TestInputEventReceiver
- private lateinit var mSender: TestInputEventSender
+ private lateinit var mReceiver: SpyInputEventReceiver
+ private lateinit var mSender: SpyInputEventSender
@Before
fun setUp() {
@@ -111,8 +71,8 @@
mHandlerThread.start()
val looper = mHandlerThread.getLooper()
- mSender = TestInputEventSender(channels[0], looper)
- mReceiver = TestInputEventReceiver(channels[1], looper)
+ mSender = SpyInputEventSender(channels[0], looper)
+ mReceiver = SpyInputEventReceiver(channels[1], looper)
}
@After
@@ -122,8 +82,7 @@
@Test
fun testSendAndReceiveKey() {
- val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_A, 0 /*repeat*/)
+ val key = getTestKeyEvent()
val seq = 10
mSender.sendInputEvent(seq, key)
val receivedKey = mReceiver.getInputEvent() as KeyEvent
@@ -133,13 +92,13 @@
assertKeyEvent(key, receivedKey)
// Check sender
- assertEquals(TestInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
+ assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
}
// The timeline case is slightly unusual because it goes from InputConsumer to InputPublisher.
@Test
fun testSendAndReceiveTimeline() {
- val sent = TestInputEventSender.Timeline(
+ val sent = SpyInputEventSender.Timeline(
inputEventId = 1, gpuCompletedTime = 2, presentTime = 3)
mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
val received = mSender.getTimeline()
@@ -151,7 +110,7 @@
// event processing.
@Test
fun testSendAndReceiveInvalidTimeline() {
- val sent = TestInputEventSender.Timeline(
+ val sent = SpyInputEventSender.Timeline(
inputEventId = 1, gpuCompletedTime = 3, presentTime = 2)
mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
val received = mSender.getTimeline()
@@ -162,4 +121,41 @@
val receivedSecondTimeline = mSender.getTimeline()
assertEquals(null, receivedSecondTimeline)
}
+
+ /**
+ * If a receiver throws an exception during 'onInputEvent' execution, the 'finally' block still
+ * completes, and therefore, finishInputEvent is called. Make sure that there's no crash in the
+ * native layer in these circumstances.
+ * In this test, we are reusing the 'mHandlerThread', but we are creating new sender and
+ * receiver.
+ */
+ @Test
+ fun testCrashingReceiverDoesNotCrash() {
+ val channels = InputChannel.openInputChannelPair("TestChannel2")
+ val sender = SpyInputEventSender(channels[0], mHandlerThread.getLooper())
+
+ // Need a separate thread for the receiver so that the sender can still get the response
+ // after the receiver crashes
+ val receiverThread = HandlerThread("Receive input events")
+ receiverThread.start()
+ val crashingReceiver = CrashingInputEventReceiver(channels[1], receiverThread.getLooper())
+ receiverThread.setUncaughtExceptionHandler { thread, exception ->
+ if (thread == receiverThread && exception is IllegalArgumentException) {
+ // do nothing - this is the exception that we need to ignore
+ } else {
+ throw exception
+ }
+ }
+
+ val key = getTestKeyEvent()
+ val seq = 11
+ sender.sendInputEvent(seq, key)
+ val finishedSignal = sender.getFinishedSignal()
+ assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
+
+ // Clean up
+ crashingReceiver.dispose()
+ sender.dispose()
+ receiverThread.quitSafely()
+ }
}
diff --git a/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
new file mode 100644
index 0000000..1099878
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.test.input
+
+import android.os.HandlerThread
+import android.view.InputChannel
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.WindowManagerPolicyConstants.PointerEventListener
+
+import com.android.server.UiThread
+import com.android.server.wm.PointerEventDispatcher
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+private class CrashingPointerEventListener : PointerEventListener {
+ override fun onPointerEvent(motionEvent: MotionEvent) {
+ throw IllegalArgumentException("This listener crashes when input event occurs")
+ }
+}
+
+class PointerEventDispatcherTest {
+ companion object {
+ private const val TAG = "PointerEventDispatcherTest"
+ }
+ private val mHandlerThread = HandlerThread("Process input events")
+ private lateinit var mSender: SpyInputEventSender
+ private lateinit var mPointerEventDispatcher: PointerEventDispatcher
+ private val mListener = CrashingPointerEventListener()
+
+ @Before
+ fun setUp() {
+ val channels = InputChannel.openInputChannelPair("TestChannel")
+
+ mHandlerThread.start()
+ val looper = mHandlerThread.getLooper()
+ mSender = SpyInputEventSender(channels[0], looper)
+
+ mPointerEventDispatcher = PointerEventDispatcher(channels[1])
+ mPointerEventDispatcher.registerInputEventListener(mListener)
+ }
+
+ @After
+ fun tearDown() {
+ mHandlerThread.quitSafely()
+ }
+
+ @Test
+ fun testSendMotionToCrashingListenerDoesNotCrash() {
+ // The exception will occur on the UiThread, so we can't catch it here on the test thread
+ UiThread.get().setUncaughtExceptionHandler { thread, exception ->
+ if (thread == UiThread.get() && exception is IllegalArgumentException) {
+ // do nothing - this is the exception that we need to ignore
+ } else {
+ throw exception
+ }
+ }
+
+ // The MotionEvent properties aren't important for this test, as long as the event
+ // is a pointer event, so that it gets processed by CrashingPointerEventListener
+ val downTime = 0L
+ val motionEvent = MotionEvent.obtain(downTime, downTime,
+ MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 0 /* metaState */)
+ motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
+ val seq = 10
+ mSender.sendInputEvent(seq, motionEvent)
+ val finishedSignal = mSender.getFinishedSignal()
+
+ // Since the listener raises an exception during the event handling, the event should be
+ // marked as 'not handled'.
+ assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = false), finishedSignal)
+ // Ensure that there aren't double finish calls. This would crash if there's a call
+ // to finish twice.
+ assertNull(mSender.getFinishedSignal())
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
new file mode 100644
index 0000000..2d9af9a
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.test.input
+
+import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS
+import android.os.Looper
+import android.view.InputChannel
+import android.view.InputEvent
+import android.view.InputEventReceiver
+import android.view.InputEventSender
+import android.view.KeyEvent
+import android.view.MotionEvent
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+
+private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T? {
+ return queue.poll(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS)
+}
+
+class SpyInputEventReceiver(channel: InputChannel, looper: Looper) :
+ InputEventReceiver(channel, looper) {
+ private val mInputEvents = LinkedBlockingQueue<InputEvent>()
+
+ override fun onInputEvent(event: InputEvent) {
+ when (event) {
+ is KeyEvent -> mInputEvents.put(KeyEvent.obtain(event))
+ is MotionEvent -> mInputEvents.put(MotionEvent.obtain(event))
+ else -> throw Exception("Received $event is neither a key nor a motion")
+ }
+ finishInputEvent(event, true /*handled*/)
+ }
+
+ fun getInputEvent(): InputEvent? {
+ return getEvent(mInputEvents)
+ }
+}
+
+class SpyInputEventSender(channel: InputChannel, looper: Looper) :
+ InputEventSender(channel, looper) {
+ data class FinishedSignal(val seq: Int, val handled: Boolean)
+ data class Timeline(val inputEventId: Int, val gpuCompletedTime: Long, val presentTime: Long)
+
+ private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>()
+ private val mTimelines = LinkedBlockingQueue<Timeline>()
+
+ override fun onInputEventFinished(seq: Int, handled: Boolean) {
+ mFinishedSignals.put(FinishedSignal(seq, handled))
+ }
+
+ override fun onTimelineReported(inputEventId: Int, gpuCompletedTime: Long, presentTime: Long) {
+ mTimelines.put(Timeline(inputEventId, gpuCompletedTime, presentTime))
+ }
+
+ fun getFinishedSignal(): FinishedSignal? {
+ return getEvent(mFinishedSignals)
+ }
+
+ fun getTimeline(): Timeline? {
+ return getEvent(mTimelines)
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/util/ParcellingTests.java b/tests/Internal/src/com/android/internal/util/ParcellingTests.java
new file mode 100644
index 0000000..65a3436
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/util/ParcellingTests.java
@@ -0,0 +1,77 @@
+/*
+ * 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.internal.util;
+
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.Parcelling.BuiltIn.ForInstant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.Instant;
+
+/** Tests for {@link Parcelling}. */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ParcellingTests {
+
+ private Parcel mParcel = Parcel.obtain();
+
+ @Test
+ public void forInstant_normal() {
+ testForInstant(Instant.ofEpochSecond(500L, 10));
+ }
+
+ @Test
+ public void forInstant_minimum() {
+ testForInstant(Instant.MIN);
+ }
+
+ @Test
+ public void forInstant_maximum() {
+ testForInstant(Instant.MAX);
+ }
+
+ @Test
+ public void forInstant_null() {
+ testForInstant(null);
+ }
+
+ private void testForInstant(Instant instant) {
+ Parcelling<Instant> parcelling = new ForInstant();
+ parcelling.parcel(instant, mParcel, 0);
+ mParcel.setDataPosition(0);
+
+ Instant created = parcelling.unparcel(mParcel);
+
+ if (instant == null) {
+ assertNull(created);
+ } else {
+ assertEquals(instant, created);
+ }
+ }
+
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 937f9dc..15de226 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -119,7 +119,7 @@
@Test
public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
@@ -131,7 +131,7 @@
@Test
public void testNewNetworkTriggersMigration() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
mTestLooper.dispatchAll();
@@ -143,7 +143,7 @@
@Test
public void testSameNetworkDoesNotTriggerMigration() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
mTestLooper.dispatchAll();
@@ -203,7 +203,7 @@
triggerChildOpened();
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
getChildSessionCallback()
.onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index d1f3a21..3c70759 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -64,7 +64,7 @@
@Test
public void testNullNetworkTriggersDisconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
@@ -76,7 +76,7 @@
@Test
public void testNewNetworkTriggersReconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
mTestLooper.dispatchAll();
@@ -89,7 +89,7 @@
@Test
public void testSameNetworkDoesNotTriggerReconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 2056eea..f3eb82f 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -78,7 +78,7 @@
@Test
public void testNetworkChangesTriggerStateTransitions() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
mTestLooper.dispatchAll();
@@ -89,7 +89,7 @@
@Test
public void testNullNetworkDoesNotTriggerStateTransition() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 1c85979..6568cdd 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -58,7 +58,7 @@
@Test
public void testNewNetworkTriggerRetry() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
mTestLooper.dispatchAll();
@@ -72,7 +72,7 @@
@Test
public void testSameNetworkDoesNotTriggerRetry() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
mTestLooper.dispatchAll();
@@ -86,7 +86,7 @@
@Test
public void testNullNetworkTriggersDisconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 2b0037e..b9dfda3 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -59,7 +59,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import org.junit.Before;
import org.junit.Test;
@@ -238,14 +238,14 @@
}
@Test
- public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
+ public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkController() {
verifyWakeLockSetUp();
final TelephonySubscriptionSnapshot updatedSnapshot =
mock(TelephonySubscriptionSnapshot.class);
mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot);
- verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot));
+ verify(mUnderlyingNetworkController).updateSubscriptionSnapshot(eq(updatedSnapshot));
verifyWakeLockAcquired();
mTestLooper.dispatchAll();
@@ -256,13 +256,13 @@
@Test
public void testNonNullUnderlyingNetworkRecordUpdateCancelsAlarm() {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */);
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
verify(mDisconnectRequestAlarm).cancel();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 64d0bca..8a0af2d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -16,7 +16,6 @@
package com.android.server.vcn;
-import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
@@ -62,6 +61,8 @@
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
@@ -137,7 +138,7 @@
@NonNull protected final VcnGatewayConnectionConfig mConfig;
@NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
- @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+ @NonNull protected final UnderlyingNetworkController mUnderlyingNetworkController;
@NonNull protected final VcnWakeLock mWakeLock;
@NonNull protected final WakeupMessage mTeardownTimeoutAlarm;
@NonNull protected final WakeupMessage mDisconnectRequestAlarm;
@@ -158,7 +159,7 @@
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
- mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
+ mUnderlyingNetworkController = mock(UnderlyingNetworkController.class);
mWakeLock = mock(VcnWakeLock.class);
mTeardownTimeoutAlarm = mock(WakeupMessage.class);
mDisconnectRequestAlarm = mock(WakeupMessage.class);
@@ -176,9 +177,9 @@
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
- doReturn(mUnderlyingNetworkTracker)
+ doReturn(mUnderlyingNetworkController)
.when(mDeps)
- .newUnderlyingNetworkTracker(any(), any(), any(), any());
+ .newUnderlyingNetworkController(any(), any(), any(), any());
doReturn(mWakeLock)
.when(mDeps)
.newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any());
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
similarity index 86%
rename from tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
rename to tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index 5af69b5..c954cb8 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.server.vcn;
+package com.android.server.vcn.routeselection;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -48,10 +50,11 @@
import android.util.ArraySet;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkListener;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.VcnNetworkProvider;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener;
import org.junit.Before;
import org.junit.Test;
@@ -64,7 +67,7 @@
import java.util.Set;
import java.util.UUID;
-public class UnderlyingNetworkTrackerTest {
+public class UnderlyingNetworkControllerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int INITIAL_SUB_ID_1 = 1;
private static final int INITIAL_SUB_ID_2 = 2;
@@ -102,14 +105,14 @@
@Mock private TelephonyManager mTelephonyManager;
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
- @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
+ @Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb;
@Mock private Network mNetwork;
@Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor;
private TestLooper mTestLooper;
private VcnContext mVcnContext;
- private UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+ private UnderlyingNetworkController mUnderlyingNetworkController;
@Before
public void setUp() {
@@ -140,12 +143,9 @@
when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
- mUnderlyingNetworkTracker =
- new UnderlyingNetworkTracker(
- mVcnContext,
- SUB_GROUP,
- mSubscriptionSnapshot,
- mNetworkTrackerCb);
+ mUnderlyingNetworkController =
+ new UnderlyingNetworkController(
+ mVcnContext, SUB_GROUP, mSubscriptionSnapshot, mNetworkControllerCb);
}
private void resetVcnContext() {
@@ -181,11 +181,8 @@
mVcnNetworkProvider,
true /* isInTestMode */);
- new UnderlyingNetworkTracker(
- vcnContext,
- SUB_GROUP,
- mSubscriptionSnapshot,
- mNetworkTrackerCb);
+ new UnderlyingNetworkController(
+ vcnContext, SUB_GROUP, mSubscriptionSnapshot, mNetworkControllerCb);
verify(cm)
.registerNetworkCallback(
@@ -233,7 +230,7 @@
mock(TelephonySubscriptionSnapshot.class);
when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
- mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
+ mUnderlyingNetworkController.updateSubscriptionSnapshot(subscriptionUpdate);
// verify that initially-filed bringup requests are unregistered (cell + wifi)
verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 3))
@@ -255,7 +252,7 @@
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSubscriptionIds(netCapsSubIds)
- .setSignalStrength(UnderlyingNetworkTracker.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
+ .setSignalStrength(WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
.build();
}
@@ -264,7 +261,7 @@
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSubscriptionIds(netCapsSubIds)
- .setSignalStrength(UnderlyingNetworkTracker.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
+ .setSignalStrength(WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
.build();
}
@@ -304,7 +301,7 @@
@Test
public void testTeardown() {
- mUnderlyingNetworkTracker.teardown();
+ mUnderlyingNetworkController.teardown();
// Expect 5 NetworkBringupCallbacks to be unregistered: 1 for WiFi, 2 for Cellular (1x for
// each subId), and 1 for each of the Wifi signal strength thresholds
@@ -368,7 +365,7 @@
networkCapabilities,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
return cb;
}
@@ -384,7 +381,7 @@
UPDATED_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -399,7 +396,7 @@
INITIAL_NETWORK_CAPABILITIES,
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -414,11 +411,13 @@
SUSPENDED_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb, times(1))
+ .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES);
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb, times(1))
+ .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -434,11 +433,13 @@
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb, times(1))
+ .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb, times(1))
+ .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -453,7 +454,7 @@
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
true /* isBlocked */);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -462,7 +463,7 @@
cb.onLost(mNetwork);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(null);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(null);
}
@Test
@@ -471,20 +472,20 @@
cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
- // Verify no more calls to the UnderlyingNetworkTrackerCallback when the
+ // Verify no more calls to the UnderlyingNetworkControllerCallback when the
// UnderlyingNetworkRecord does not actually change
- verifyNoMoreInteractions(mNetworkTrackerCb);
+ verifyNoMoreInteractions(mNetworkControllerCb);
}
@Test
public void testRecordTrackerCallbackNotifiedAfterTeardown() {
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
- mUnderlyingNetworkTracker.teardown();
+ mUnderlyingNetworkController.teardown();
cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
// Verify that the only call was during onAvailable()
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(any());
+ verify(mNetworkControllerCb, times(1)).onSelectedUnderlyingNetworkChanged(any());
}
// TODO (b/187991063): Add tests for network prioritization
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 740b44e..bd0a4bc 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -170,16 +170,6 @@
}
// ==========================================================
-// Build the host shared library: aapt2_jni
-// ==========================================================
-cc_library_host_shared {
- name: "libaapt2_jni",
- srcs: toolSources + ["jni/aapt2_jni.cpp"],
- static_libs: ["libaapt2"],
- defaults: ["aapt2_defaults"],
-}
-
-// ==========================================================
// Build the host tests: aapt2_tests
// ==========================================================
cc_test_host {
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 40bbb36..9828b97 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1461,6 +1461,64 @@
}
};
+/** Represents <sdk-library> elements. **/
+class SdkLibrary : public ManifestExtractor::Element {
+ public:
+ SdkLibrary() = default;
+ std::string name;
+ int versionMajor;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ printer->Print(
+ StringPrintf("sdk-library: name='%s' versionMajor='%d'\n", name.data(), versionMajor));
+ }
+};
+
+/** Represents <uses-sdk-library> elements. **/
+class UsesSdkLibrary : public ManifestExtractor::Element {
+ public:
+ UsesSdkLibrary() = default;
+ std::string name;
+ int versionMajor;
+ std::vector<std::string> certDigests;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ AddCertDigest(element);
+ }
+ }
+
+ void AddCertDigest(xml::Element* element) {
+ std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+ if (!digest.empty()) {
+ certDigests.push_back(digest);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ printer->Print(
+ StringPrintf("uses-sdk-library: name='%s' versionMajor='%d'", name.data(), versionMajor));
+ for (size_t i = 0; i < certDigests.size(); i++) {
+ printer->Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+ }
+ printer->Print("\n");
+ }
+};
+
/** Represents <uses-native-library> elements. **/
class UsesNativeLibrary : public ManifestExtractor::Element {
public:
@@ -2367,6 +2425,7 @@
{"required-not-feature", std::is_base_of<RequiredNotFeature, T>::value},
{"screen", std::is_base_of<Screen, T>::value},
{"service", std::is_base_of<Service, T>::value},
+ {"sdk-library", std::is_base_of<SdkLibrary, T>::value},
{"static-library", std::is_base_of<StaticLibrary, T>::value},
{"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
{"supports-input", std::is_base_of<SupportsInput, T>::value},
@@ -2379,6 +2438,7 @@
{"uses-permission", std::is_base_of<UsesPermission, T>::value},
{"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"uses-sdk-library", std::is_base_of<UsesSdkLibrary, T>::value},
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
};
@@ -2421,6 +2481,7 @@
{"required-not-feature", &CreateType<RequiredNotFeature>},
{"screen", &CreateType<Screen>},
{"service", &CreateType<Service>},
+ {"sdk-library", &CreateType<SdkLibrary>},
{"static-library", &CreateType<StaticLibrary>},
{"supports-gl-texture", &CreateType<SupportsGlTexture>},
{"supports-input", &CreateType<SupportsInput>},
@@ -2433,6 +2494,7 @@
{"uses-permission", &CreateType<UsesPermission>},
{"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
+ {"uses-sdk-library", &CreateType<UsesSdkLibrary>},
{"uses-static-library", &CreateType<UsesStaticLibrary>},
};
diff --git a/tools/aapt2/jni/ScopedUtfChars.h b/tools/aapt2/jni/ScopedUtfChars.h
deleted file mode 100644
index a8c4b13..0000000
--- a/tools/aapt2/jni/ScopedUtfChars.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SCOPED_UTF_CHARS_H_included
-#define SCOPED_UTF_CHARS_H_included
-
-#include <string.h>
-#include <jni.h>
-
-#include "android-base/logging.h"
-
-// This file was copied with some minor modifications from libnativehelper.
-// As soon as libnativehelper can be compiled for Windows, this file should be
-// replaced with libnativehelper's implementation.
-class ScopedUtfChars {
- public:
- ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
- CHECK(s != nullptr);
- utf_chars_ = env->GetStringUTFChars(s, nullptr);
- }
-
- ScopedUtfChars(ScopedUtfChars&& rhs) :
- env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
- rhs.env_ = nullptr;
- rhs.string_ = nullptr;
- rhs.utf_chars_ = nullptr;
- }
-
- ~ScopedUtfChars() {
- if (utf_chars_) {
- env_->ReleaseStringUTFChars(string_, utf_chars_);
- }
- }
-
- ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
- if (this != &rhs) {
- // Delete the currently owned UTF chars.
- this->~ScopedUtfChars();
-
- // Move the rhs ScopedUtfChars and zero it out.
- env_ = rhs.env_;
- string_ = rhs.string_;
- utf_chars_ = rhs.utf_chars_;
- rhs.env_ = nullptr;
- rhs.string_ = nullptr;
- rhs.utf_chars_ = nullptr;
- }
- return *this;
- }
-
- const char* c_str() const {
- return utf_chars_;
- }
-
- size_t size() const {
- return strlen(utf_chars_);
- }
-
- const char& operator[](size_t n) const {
- return utf_chars_[n];
- }
-
- private:
- JNIEnv* env_;
- jstring string_;
- const char* utf_chars_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
-};
-
-#endif // SCOPED_UTF_CHARS_H_included
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
deleted file mode 100644
index ec3c543..0000000
--- a/tools/aapt2/jni/aapt2_jni.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "com_android_tools_aapt2_Aapt2Jni.h"
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "android-base/logging.h"
-#include "ScopedUtfChars.h"
-
-#include "Diagnostics.h"
-#include "cmd/Compile.h"
-#include "cmd/Link.h"
-#include "util/Util.h"
-
-using android::StringPiece;
-
-/*
- * Converts a java List<String> into C++ vector<ScopedUtfChars>.
- */
-static std::vector<ScopedUtfChars> list_to_utfchars(JNIEnv *env, jobject obj) {
- std::vector<ScopedUtfChars> converted;
-
- // Call size() method on the list to know how many elements there are.
- jclass list_cls = env->GetObjectClass(obj);
- jmethodID size_method_id = env->GetMethodID(list_cls, "size", "()I");
- CHECK(size_method_id != 0);
- jint size = env->CallIntMethod(obj, size_method_id);
- CHECK(size >= 0);
-
- // Now, iterate all strings in the list
- // (note: generic erasure means get() return an Object)
- jmethodID get_method_id = env->GetMethodID(list_cls, "get", "(I)Ljava/lang/Object;");
- CHECK(get_method_id != 0);
- for (jint i = 0; i < size; i++) {
- // Call get(i) to get the string in the ith position.
- jobject string_obj_uncast = env->CallObjectMethod(obj, get_method_id, i);
- CHECK(string_obj_uncast != nullptr);
- jstring string_obj = static_cast<jstring>(string_obj_uncast);
- converted.push_back(ScopedUtfChars(env, string_obj));
- }
-
- return converted;
-}
-
-/*
- * Extracts all StringPiece from the ScopedUtfChars instances.
- *
- * The returned pieces can only be used while the original ones have not been
- * destroyed.
- */
-static std::vector<StringPiece> extract_pieces(const std::vector<ScopedUtfChars> &strings) {
- std::vector<StringPiece> pieces;
-
- std::for_each(
- strings.begin(), strings.end(),
- [&pieces](const ScopedUtfChars &p) { pieces.push_back(p.c_str()); });
-
- return pieces;
-}
-
-class JniDiagnostics : public aapt::IDiagnostics {
- public:
- JniDiagnostics(JNIEnv* env, jobject diagnostics_obj)
- : env_(env), diagnostics_obj_(diagnostics_obj) {
- mid_ = NULL;
- }
-
- void Log(Level level, aapt::DiagMessageActual& actual_msg) override {
- jint level_value;
- switch (level) {
- case Level::Error:
- level_value = 3;
- break;
-
- case Level::Warn:
- level_value = 2;
- break;
-
- case Level::Note:
- level_value = 1;
- break;
- }
- jstring message = env_->NewStringUTF(actual_msg.message.c_str());
- jstring path = env_->NewStringUTF(actual_msg.source.path.c_str());
- jlong line = -1;
- if (actual_msg.source.line) {
- line = actual_msg.source.line.value();
- }
- if (!mid_) {
- jclass diagnostics_cls = env_->GetObjectClass(diagnostics_obj_);
- mid_ = env_->GetMethodID(diagnostics_cls, "log", "(ILjava/lang/String;JLjava/lang/String;)V");
- }
- env_->CallVoidMethod(diagnostics_obj_, mid_, level_value, path, line, message);
- }
-
- private:
- JNIEnv* env_;
- jobject diagnostics_obj_;
- jmethodID mid_;
- DISALLOW_COPY_AND_ASSIGN(JniDiagnostics);
-};
-
-JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeCompile(
- JNIEnv* env, jclass aapt_obj, jobject arguments_obj, jobject diagnostics_obj) {
- std::vector<ScopedUtfChars> compile_args_jni =
- list_to_utfchars(env, arguments_obj);
- std::vector<StringPiece> compile_args = extract_pieces(compile_args_jni);
- JniDiagnostics diagnostics(env, diagnostics_obj);
- return aapt::CompileCommand(&diagnostics).Execute(compile_args, &std::cerr);
-}
-
-JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv* env,
- jclass aapt_obj,
- jobject arguments_obj,
- jobject diagnostics_obj) {
- std::vector<ScopedUtfChars> link_args_jni =
- list_to_utfchars(env, arguments_obj);
- std::vector<StringPiece> link_args = extract_pieces(link_args_jni);
- JniDiagnostics diagnostics(env, diagnostics_obj);
- return aapt::LinkCommand(&diagnostics).Execute(link_args, &std::cerr);
-}
-
-JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping(
- JNIEnv *env, jclass aapt_obj) {
- // This is just a no-op method to see if the library has been loaded.
-}
diff --git a/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h b/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h
deleted file mode 100644
index 3cd9865..0000000
--- a/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class com_android_tools_aapt2_Aapt2Jni */
-
-#ifndef _Included_com_android_tools_aapt2_Aapt2Jni
-#define _Included_com_android_tools_aapt2_Aapt2Jni
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class: com_android_tools_aapt2_Aapt2Jni
- * Method: ping
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping
- (JNIEnv *, jclass);
-
-/*
- * Class: com_android_tools_aapt2_Aapt2Jni
- * Method: nativeCompile
- * Signature: (Ljava/util/List;Lcom/android/tools/aapt2/Aapt2JniDiagnostics;)I
- */
-JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeCompile(JNIEnv*, jclass, jobject,
- jobject);
-
-/*
- * Class: com_android_tools_aapt2_Aapt2Jni
- * Method: nativeLink
- * Signature: (Ljava/util/List;Lcom/android/tools/aapt2/Aapt2JniDiagnostics;)I
- */
-JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv*, jclass, jobject,
- jobject);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 63b2fcd..b46a125 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -508,6 +508,16 @@
uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
uses_static_library_action["additional-certificate"];
+ xml::XmlNodeAction& sdk_library_action = application_action["sdk-library"];
+ sdk_library_action.Action(RequiredNameIsJavaPackage);
+ sdk_library_action.Action(RequiredAndroidAttribute("versionMajor"));
+
+ xml::XmlNodeAction& uses_sdk_library_action = application_action["uses-sdk-library"];
+ uses_sdk_library_action.Action(RequiredNameIsJavaPackage);
+ uses_sdk_library_action.Action(RequiredAndroidAttribute("versionMajor"));
+ uses_sdk_library_action.Action(RequiredAndroidAttribute("certDigest"));
+ uses_sdk_library_action["additional-certificate"];
+
xml::XmlNodeAction& uses_package_action = application_action["uses-package"];
uses_package_action.Action(RequiredNameIsJavaPackage);
uses_package_action["additional-certificate"];