Merge "Add component types which a process is hosting into the proc memstats" into tm-dev
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index e56ed39..581367a 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -733,7 +733,7 @@
return -errno;
}
- const std::set<int> hal_pids = get_interesting_hal_pids();
+ const std::set<int> hal_pids = get_interesting_pids();
auto pooledBuffer = get_buffer_from_pool();
ProtoOutputStream proto(pooledBuffer);
diff --git a/core/java/android/app/ComponentOptions.java b/core/java/android/app/ComponentOptions.java
index d50a73a..4e5e384 100644
--- a/core/java/android/app/ComponentOptions.java
+++ b/core/java/android/app/ComponentOptions.java
@@ -38,7 +38,15 @@
public static final String KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED =
"android.pendingIntent.backgroundActivityAllowed";
+ /**
+ * PendingIntent caller allows activity to be started if caller has BAL permission.
+ * @hide
+ */
+ public static final String KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION =
+ "android.pendingIntent.backgroundActivityAllowedByPermission";
+
private boolean mPendingIntentBalAllowed = PENDING_INTENT_BAL_ALLOWED_DEFAULT;
+ private boolean mPendingIntentBalAllowedByPermission = false;
ComponentOptions() {
}
@@ -50,6 +58,9 @@
setPendingIntentBackgroundActivityLaunchAllowed(
opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
PENDING_INTENT_BAL_ALLOWED_DEFAULT));
+ setPendingIntentBackgroundActivityLaunchAllowedByPermission(
+ opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION,
+ false));
}
/**
@@ -68,9 +79,30 @@
return mPendingIntentBalAllowed;
}
+ /**
+ * Set PendingIntent activity can be launched from background if caller has BAL permission.
+ * @hide
+ */
+ public void setPendingIntentBackgroundActivityLaunchAllowedByPermission(boolean allowed) {
+ mPendingIntentBalAllowedByPermission = allowed;
+ }
+
+ /**
+ * Get PendingIntent activity is allowed to be started in the background if the caller
+ * has BAL permission.
+ * @hide
+ */
+ public boolean isPendingIntentBackgroundActivityLaunchAllowedByPermission() {
+ return mPendingIntentBalAllowedByPermission;
+ }
+
public Bundle toBundle() {
Bundle b = new Bundle();
b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
+ if (mPendingIntentBalAllowedByPermission) {
+ b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION,
+ mPendingIntentBalAllowedByPermission);
+ }
return b;
}
}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 1b87945..7910f1a 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -34,10 +34,7 @@
import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
-import android.os.RemoteException;
-import android.util.Log;
import android.view.DisplayCutout;
-import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import java.lang.annotation.Retention;
@@ -355,20 +352,6 @@
return isVisible;
}
- /**
- * @param isLowResolution
- * @return
- * @hide
- */
- public TaskSnapshot getTaskSnapshot(boolean isLowResolution) {
- try {
- return ActivityTaskManager.getService().getTaskSnapshot(taskId, isLowResolution);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get task snapshot, taskId=" + taskId, e);
- return null;
- }
- }
-
/** @hide */
@NonNull
@TestApi
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 067a4c3..ef46624 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -36,7 +36,6 @@
import com.android.internal.graphics.palette.CelebiQuantizer;
import com.android.internal.graphics.palette.Palette;
import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
-import com.android.internal.util.ContrastColorUtil;
import java.io.FileOutputStream;
import java.lang.annotation.Retention;
@@ -94,18 +93,10 @@
// using the area instead. This way our comparisons are aspect ratio independent.
private static final int MAX_WALLPAPER_EXTRACTION_AREA = MAX_BITMAP_SIZE * MAX_BITMAP_SIZE;
- // When extracting the main colors, only consider colors
- // present in at least MIN_COLOR_OCCURRENCE of the image
- private static final float MIN_COLOR_OCCURRENCE = 0.05f;
-
- // Decides when dark theme is optimal for this wallpaper
- private static final float DARK_THEME_MEAN_LUMINANCE = 0.3f;
- // Minimum mean luminosity that an image needs to have to support dark text
- private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.7f;
- // We also check if the image has dark pixels in it,
- // to avoid bright images with some dark spots.
- private static final float DARK_PIXEL_CONTRAST = 5.5f;
- private static final float MAX_DARK_AREA = 0.05f;
+ // Decides when dark theme is optimal for this wallpaper.
+ // The midpoint of perceptual luminance, 50, is 18.42 in relative luminance.
+ // ColorUtils.calculateLuminance returns relative luminance on a scale from 0 to 1.
+ private static final float DARK_THEME_MEAN_LUMINANCE = 0.1842f;
private final List<Color> mMainColors;
private final Map<Integer, Integer> mAllColors;
@@ -253,12 +244,9 @@
this(primaryColor, secondaryColor, tertiaryColor, 0);
// Calculate dark theme support based on primary color.
- final float[] tmpHsl = new float[3];
- ColorUtils.colorToHSL(primaryColor.toArgb(), tmpHsl);
- final float luminance = tmpHsl[2];
- if (luminance < DARK_THEME_MEAN_LUMINANCE) {
- mColorHints |= HINT_SUPPORTS_DARK_THEME;
- }
+ final double relativeLuminance = ColorUtils.calculateLuminance(primaryColor.toArgb());
+ final boolean wallpaperIsDark = relativeLuminance < DARK_THEME_MEAN_LUMINANCE;
+ mColorHints |= wallpaperIsDark ? HINT_SUPPORTS_DARK_THEME : HINT_SUPPORTS_DARK_TEXT;
}
/**
@@ -536,9 +524,6 @@
dimAmount = MathUtils.saturate(dimAmount);
int[] pixels = new int[source.getWidth() * source.getHeight()];
- double totalLuminance = 0;
- final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
- int darkPixels = 0;
source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
source.getWidth(), source.getHeight());
@@ -547,42 +532,70 @@
int dimmingLayerAlpha = (int) (255 * dimAmount);
int blackTransparent = ColorUtils.setAlphaComponent(Color.BLACK, dimmingLayerAlpha);
- // This bitmap was already resized to fit the maximum allowed area.
- // Let's just loop through the pixels, no sweat!
- float[] tmpHsl = new float[3];
+ // The median luminance in the wallpaper will be used to decide if it is light or dark.
+ //
+ // Calculating the luminances, adding them to a data structure, then selecting
+ // the middle element would be expensive, the sort would be O(n), where n is the number
+ // of pixels.
+ //
+ // Instead, we create an integer array with 101 elements initialized to zero.
+ // Why 101? 0 through 100, inclusive, matching the range of luminance.
+ // Then, for each pixel, the luminance is calculated, and the integer at the array index
+ // equal to the rounded luminance is incremented.
+ //
+ // After processing the pixels, the median luminance is determined by iterating over the
+ // array containing the count for each luminance. Starting from 0, we adding the count at
+ // each index until pixels.length/2 is exceeded. When that occurs, it means the current
+ // array index contains the pixel of median luminance, thus the current array index is the
+ // median luminance.
+ int[] luminanceCounts = new int[101];
for (int i = 0; i < pixels.length; i++) {
int pixelColor = pixels[i];
- ColorUtils.colorToHSL(pixelColor, tmpHsl);
final int alpha = Color.alpha(pixelColor);
-
- // Apply composite colors where the foreground is a black layer with an alpha value of
- // the dim amount and the background is the wallpaper pixel color.
- int compositeColors = ColorUtils.compositeColors(blackTransparent, pixelColor);
-
- // Calculate the adjusted luminance of the dimmed wallpaper pixel color.
- double adjustedLuminance = ColorUtils.calculateLuminance(compositeColors);
-
- // Make sure we don't have a dark pixel mass that will
- // make text illegible.
- final boolean satisfiesTextContrast = ContrastColorUtil
- .calculateContrast(pixelColor, Color.BLACK) > DARK_PIXEL_CONTRAST;
- if (!satisfiesTextContrast && alpha != 0) {
- darkPixels++;
- if (DEBUG_DARK_PIXELS) {
- pixels[i] = Color.RED;
- }
+ if (alpha == 0) {
+ continue;
}
- totalLuminance += adjustedLuminance;
+
+ // If the wallpaper is dimmed, apply dimming before calculating luminance.
+ int compositeColor = dimAmount <= 0 ? pixelColor :
+ ColorUtils.compositeColors(blackTransparent, pixelColor);
+
+ // calculateLuminance will return relative luminance on a scale from 0 to 1. Intent
+ // is normalize to 0 to 100, and that is done by defensively normalizing to
+ // luminanceCounts.length, then flooring the result to defensively avoid any imprecision
+ // in the result of calculateLuminance that could cause it to exceed 1 and thus the
+ // array bounds.
+ float relativeLuminance = (float) ColorUtils.calculateLuminance(compositeColor)
+ * luminanceCounts.length - 1;
+ int intRelativeLuminance = (int) Math.floor(relativeLuminance);
+ int clampedRelativeLuminance = (intRelativeLuminance < 0) ? 0 :
+ (intRelativeLuminance > luminanceCounts.length - 1) ? luminanceCounts.length - 1
+ : intRelativeLuminance;
+ luminanceCounts[clampedRelativeLuminance] += 1;
}
+ int criticalRelativeLuminance = 0;
+ int luminancesProcessed = 0;
+ int criticalLuminanceIndex = (int) Math.floor(pixels.length * 0.5);
+ for (int i = 0; i < 100; i++) {
+ luminancesProcessed += luminanceCounts[i];
+ if (luminancesProcessed > criticalLuminanceIndex) {
+ criticalRelativeLuminance = i;
+ break;
+ }
+ }
+
+ // Wallpaper is dark if the median pixel is less than mid-gray.
+ //
+ // Relative luminance places mid-gray at 18.42, 19 is used since luminances were rounded to
+ // ints to be stored in the array.
+ //
+ // Why not use perceptual luminance? It would require one more conversion step at each
+ // pixel, or adding a function to convert relative luminance to perceptual luminance just
+ // for this line.
+ boolean wallpaperIsDark = criticalRelativeLuminance < 19;
int hints = 0;
- double meanLuminance = totalLuminance / pixels.length;
- if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) {
- hints |= HINT_SUPPORTS_DARK_TEXT;
- }
- if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) {
- hints |= HINT_SUPPORTS_DARK_THEME;
- }
+ hints |= wallpaperIsDark ? HINT_SUPPORTS_DARK_THEME : HINT_SUPPORTS_DARK_TEXT;
if (DEBUG_DARK_PIXELS) {
try (FileOutputStream out = new FileOutputStream("/data/pixels.png")) {
@@ -592,8 +605,8 @@
} catch (Exception e) {
e.printStackTrace();
}
- Log.d("WallpaperColors", "l: " + meanLuminance + ", d: " + darkPixels +
- " maxD: " + maxDarkPixels + " numPixels: " + pixels.length);
+ Log.d("WallpaperColors", "median relative L: " + criticalRelativeLuminance
+ + ", numPixels: " + pixels.length);
}
return hints;
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index a1640ee..5418f7e 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -19,6 +19,8 @@
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.VirtualDeviceParams;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
/**
* Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService.
@@ -42,4 +44,16 @@
IVirtualDevice createVirtualDevice(
in IBinder token, String packageName, int associationId,
in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener);
+
+ /**
+ * Creates a virtual display owned by a particular virtual device.
+ *
+ * @param virtualDisplayConfig The configuration used in creating the display
+ * @param callback A callback that receives display lifecycle events
+ * @param virtualDevice The device that will own this display
+ * @param packageName The package name of the calling app
+ */
+ int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig,
+ in IVirtualDisplayCallback callback, in IVirtualDevice virtualDevice,
+ String packageName);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 3bb8373..1b93bb8 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -33,6 +33,8 @@
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.VirtualDisplayFlag;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.input.VirtualKeyboard;
@@ -65,7 +67,7 @@
public final class VirtualDeviceManager {
private static final boolean DEBUG = false;
- private static final String LOG_TAG = "VirtualDeviceManager";
+ private static final String TAG = "VirtualDeviceManager";
private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
@@ -150,6 +152,7 @@
public static class VirtualDevice implements AutoCloseable {
private final Context mContext;
+ private final IVirtualDeviceManager mService;
private final IVirtualDevice mVirtualDevice;
private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners =
new ArrayMap<>();
@@ -189,6 +192,7 @@
Context context,
int associationId,
VirtualDeviceParams params) throws RemoteException {
+ mService = service;
mContext = context.getApplicationContext();
mVirtualDevice = service.createVirtualDevice(
new Binder(),
@@ -274,18 +278,23 @@
// TODO(b/205343547): Handle display groups properly instead of creating a new display
// group for every new virtual display created using this API.
// belongs to the same display group.
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- // DisplayManager will call into VirtualDeviceManagerInternal to register the
- // created displays.
- return displayManager.createVirtualDisplay(
- mVirtualDevice,
- new VirtualDisplayConfig.Builder(
- getVirtualDisplayName(), width, height, densityDpi)
- .setSurface(surface)
- .setFlags(getVirtualDisplayFlags(flags))
- .build(),
- callback,
- executor);
+ VirtualDisplayConfig config = new VirtualDisplayConfig.Builder(
+ getVirtualDisplayName(), width, height, densityDpi)
+ .setSurface(surface)
+ .setFlags(getVirtualDisplayFlags(flags))
+ .build();
+ IVirtualDisplayCallback callbackWrapper =
+ new DisplayManagerGlobal.VirtualDisplayCallback(callback, executor);
+ final int displayId;
+ try {
+ displayId = mService.createVirtualDisplay(config, callbackWrapper, mVirtualDevice,
+ mContext.getPackageName());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
+ return displayManager.createVirtualDisplayWrapper(config, mContext, callbackWrapper,
+ displayId);
}
/**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 836bff5..fce23cf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3670,7 +3670,7 @@
* <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}</li>
* <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS} and is the same
* package as the {@code service} (determined by its component's package) and the Android
- * version is at least {@link android.os.Build.VERSION_CODES#S}</li>
+ * version is at least {@link android.os.Build.VERSION_CODES#TIRAMISU}</li>
* <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS} and is in same
* profile group as the given {@code user}</li>
* <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} and is in same
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index eadcac9..b505395 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -31,7 +31,6 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.KeyguardManager;
-import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
@@ -971,17 +970,8 @@
executor = new HandlerExecutor(
Handler.createAsync(handler != null ? handler.getLooper() : Looper.myLooper()));
}
- return mGlobal.createVirtualDisplay(mContext, projection, null /* virtualDevice */,
- virtualDisplayConfig, callback, executor, windowContext);
- }
-
- /** @hide */
- public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
- @NonNull VirtualDisplayConfig virtualDisplayConfig,
- @Nullable VirtualDisplay.Callback callback,
- @Nullable Executor executor) {
- return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
- virtualDisplayConfig, callback, executor, null);
+ return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback,
+ executor, windowContext);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a62bbf6..74356dd 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PropertyInvalidatedCache;
-import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -586,18 +585,28 @@
}
public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
- IVirtualDevice virtualDevice, @NonNull VirtualDisplayConfig virtualDisplayConfig,
- VirtualDisplay.Callback callback, @Nullable Executor executor,
- @Nullable Context windowContext) {
+ @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
+ @Nullable Executor executor, @Nullable Context windowContext) {
VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, executor);
IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
int displayId;
try {
displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper,
- projectionToken, virtualDevice, context.getPackageName());
+ projectionToken, context.getPackageName());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
+ return createVirtualDisplayWrapper(virtualDisplayConfig, windowContext, callbackWrapper,
+ displayId);
+ }
+
+ /**
+ * Create a VirtualDisplay wrapper object for a newly created virtual display ; to be called
+ * once the display has been created in system_server.
+ */
+ @Nullable
+ public VirtualDisplay createVirtualDisplayWrapper(VirtualDisplayConfig virtualDisplayConfig,
+ Context windowContext, IVirtualDisplayCallback callbackWrapper, int displayId) {
if (displayId < 0) {
Log.e(TAG, "Could not create virtual display: " + virtualDisplayConfig.getName());
return null;
@@ -1050,7 +1059,10 @@
}
}
- private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
+ /**
+ * Assists in dispatching VirtualDisplay lifecycle event callbacks on a given Executor.
+ */
+ public static final class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
@Nullable private final VirtualDisplay.Callback mCallback;
@Nullable private final Executor mExecutor;
@@ -1062,7 +1074,7 @@
* @param executor The executor to call the {@code callback} on. Must not be {@code null} if
* the callback is not {@code null}.
*/
- VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
+ public VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
mCallback = callback;
mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null;
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index be482c9..1c3bec5 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.companion.virtual.IVirtualDevice;
import android.graphics.Point;
import android.hardware.SensorManager;
import android.os.Handler;
@@ -60,6 +61,14 @@
Handler handler, SensorManager sensorManager);
/**
+ * Called by the VirtualDeviceManagerService to create a VirtualDisplay owned by a
+ * VirtualDevice.
+ */
+ public abstract int createVirtualDisplay(VirtualDisplayConfig config,
+ IVirtualDisplayCallback callback, IVirtualDevice virtualDevice,
+ DisplayWindowPolicyController dwpc, String packageName);
+
+ /**
* Called by the power manager to request a new power state.
* <p>
* The display power controller makes a copy of the provided object and then
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index ddd18f4..ca3e580 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -16,7 +16,6 @@
package android.hardware.display;
-import android.companion.virtual.IVirtualDevice;
import android.content.pm.ParceledListSlice;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
@@ -88,10 +87,10 @@
void requestColorMode(int displayId, int colorMode);
// Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
- // MediaProjection token or VirtualDevice for certain combinations of flags.
+ // MediaProjection token for certain combinations of flags.
int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig,
in IVirtualDisplayCallback callback, in IMediaProjection projectionToken,
- in IVirtualDevice virtualDevice, String packageName);
+ String packageName);
// No permissions required, but must be same Uid as the creator.
void resizeVirtualDisplay(in IVirtualDisplayCallback token,
diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java
index b2bf9bc..328750f 100644
--- a/core/java/android/service/voice/AbstractHotwordDetector.java
+++ b/core/java/android/service/voice/AbstractHotwordDetector.java
@@ -198,5 +198,16 @@
HotwordDetector.Callback::onError,
mCallback));
}
+
+ @Override
+ public void onRejected(@Nullable HotwordRejectedResult result) {
+ if (result == null) {
+ result = new HotwordRejectedResult.Builder().build();
+ }
+ mHandler.sendMessage(obtainMessage(
+ HotwordDetector.Callback::onRejected,
+ mCallback,
+ result));
+ }
}
}
diff --git a/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl b/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl
index e865089..61ac68b 100644
--- a/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl
+++ b/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl
@@ -18,6 +18,7 @@
import android.media.AudioFormat;
import android.service.voice.HotwordDetectedResult;
+import android.service.voice.HotwordRejectedResult;
/**
* Callback for returning the detected result from the HotwordDetectionService.
@@ -38,4 +39,10 @@
* Called when the detection fails due to an error.
*/
void onError();
+
+ /**
+ * Called when the detected result was not detected.
+ */
+ void onRejected(
+ in HotwordRejectedResult hotwordRejectedResult);
}
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index f5a0c66..68fa130 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -164,6 +164,17 @@
HotwordDetector.Callback::onError,
mCallback));
}
+
+ @Override
+ public void onRejected(@Nullable HotwordRejectedResult result) {
+ if (result == null) {
+ result = new HotwordRejectedResult.Builder().build();
+ }
+ mHandler.sendMessage(obtainMessage(
+ HotwordDetector.Callback::onRejected,
+ mCallback,
+ result));
+ }
}
private static class InitializationStateListener
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index d6b75b9..b17e199 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -36,6 +36,7 @@
import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.util.ArraySet;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
@@ -272,11 +273,7 @@
mController.getHost().getInputMethodManager(), null /* icProto */);
}
- // We still need to let the legacy app know the visibility change even if we don't have the
- // control. If we don't have the source, we don't change the requested visibility for making
- // the callback behavior compatible.
- mController.updateCompatSysUiVisibility(
- mType, (hasControl || source == null) ? mRequestedVisible : isVisible, hasControl);
+ updateCompatSysUiVisibility(hasControl, source, isVisible);
// If we don't have control, we are not able to change the visibility.
if (!hasControl) {
@@ -294,6 +291,36 @@
return true;
}
+ private void updateCompatSysUiVisibility(boolean hasControl, InsetsSource source,
+ boolean visible) {
+ final @InsetsType int publicType = InsetsState.toPublicType(mType);
+ if (publicType != WindowInsets.Type.statusBars()
+ && publicType != WindowInsets.Type.navigationBars()) {
+ // System UI visibility only controls status bars and navigation bars.
+ return;
+ }
+ final boolean compatVisible;
+ if (hasControl) {
+ compatVisible = mRequestedVisible;
+ } else if (source != null && !source.getFrame().isEmpty()) {
+ compatVisible = visible;
+ } else {
+ final ArraySet<Integer> types = InsetsState.toInternalType(publicType);
+ for (int i = types.size() - 1; i >= 0; i--) {
+ final InsetsSource s = mState.peekSource(types.valueAt(i));
+ if (s != null && !s.getFrame().isEmpty()) {
+ // The compat system UI visibility would be updated by another consumer which
+ // handles the same public insets type.
+ return;
+ }
+ }
+ // No one provides the public type. Use the requested visibility for making the callback
+ // behavior compatible.
+ compatVisible = mRequestedVisible;
+ }
+ mController.updateCompatSysUiVisibility(mType, compatVisible, hasControl);
+ }
+
@VisibleForTesting
public boolean isRequestedVisible() {
return mRequestedVisible;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 90497e7..ba6ba63 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10000,7 +10000,9 @@
// We reset any translation state as views may be re-used (e.g., as in ListView and
// RecyclerView). We only need to do this for views important for content capture since
// views unimportant for content capture won't be translated anyway.
- clearTranslationState();
+ if (!isTemporarilyDetached()) {
+ clearTranslationState();
+ }
}
}
diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java
index 3936b63..a95d95f 100644
--- a/core/java/android/view/translation/ViewTranslationCallback.java
+++ b/core/java/android/view/translation/ViewTranslationCallback.java
@@ -52,6 +52,15 @@
* the original text instead of the translated text or use a different approach to display the
* translated text.
*
+ * <p> NOTE: In Android version {@link android.os.Build.VERSION_CODES#TIRAMISU} and later,
+ * the implementation must be able to handle a selectable {@link android.widget.TextView}
+ * (i.e., {@link android.widget.TextView#isTextSelectable()} returns {@code true}. The default
+ * callback implementation for TextView uses a {@link android.text.method.TransformationMethod}
+ * to show the translated text, which will cause a crash when the translated text is selected.
+ * Therefore, the default callback temporarily makes the TextView non-selectable while the
+ * translation text is shown. This is one approach for handling selectable TextViews a
+ * TransformationMethod is used.
+ *
* See {@link View#onViewTranslationResponse} for how to get the translated information.
*
* @return {@code true} if the View handles showing the translation.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 93f7264..e745b8c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -14240,13 +14240,13 @@
* Collects a {@link ViewTranslationRequest} which represents the content to be translated in
* the view.
*
- * <p>NOTE: When overriding the method, it should not translate the password. If the subclass
- * uses {@link TransformationMethod} to display the translated result, it's also not recommend
- * to translate text is selectable or editable.
+ * <p>NOTE: When overriding the method, it should not collect a request to translate this
+ * TextView if it is displaying a password.
*
* @param supportedFormats the supported translation format. The value could be {@link
* android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
- * @return the {@link ViewTranslationRequest} which contains the information to be translated.
+ * @param requestsCollector {@link Consumer} to receiver the {@link ViewTranslationRequest}
+ * which contains the information to be translated.
*/
@Override
public void onCreateViewTranslationRequest(@NonNull int[] supportedFormats,
@@ -14268,18 +14268,9 @@
return;
}
boolean isPassword = isAnyPasswordInputType() || hasPasswordTransformationMethod();
- // TODO(b/177214256): support selectable text translation.
- // We use the TransformationMethod to implement showing the translated text. The
- // TextView does not support the text length change for TransformationMethod. If the
- // text is selectable or editable, it will crash while selecting the text. To support
- // it, it needs broader changes to text APIs, we only allow to translate non selectable
- // and editable text in S.
- if (isTextEditable() || isPassword || isTextSelectable()) {
- if (UiTranslationController.DEBUG) {
- Log.w(LOG_TAG, "Cannot create translation request. editable = "
- + isTextEditable() + ", isPassword = " + isPassword + ", selectable = "
- + isTextSelectable());
- }
+ if (isTextEditable() || isPassword) {
+ Log.w(LOG_TAG, "Cannot create translation request. editable = "
+ + isTextEditable() + ", isPassword = " + isPassword);
return;
}
// TODO(b/176488462): apply the view's important for translation
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 1713d84..5ad4579 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -32,6 +32,8 @@
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
+import java.lang.ref.WeakReference;
+
/**
* Default implementation for {@link ViewTranslationCallback} for {@link TextView} components.
* This class handles how to display the translated information for {@link TextView}.
@@ -48,6 +50,11 @@
private boolean mIsShowingTranslation = false;
private boolean mAnimationRunning = false;
private boolean mIsTextPaddingEnabled = false;
+ private boolean mOriginalIsTextSelectable = false;
+ private int mOriginalFocusable = 0;
+ private boolean mOriginalFocusableInTouchMode = false;
+ private boolean mOriginalClickable = false;
+ private boolean mOriginalLongClickable = false;
private CharSequence mPaddedText;
private int mAnimationDurationMillis = 250; // default value
@@ -81,21 +88,50 @@
// update the translation response to keep the result up to date.
// Because TextView.setTransformationMethod() will skip the same TransformationMethod
// instance, we should create a new one to let new translation can work.
+ TextView theTextView = (TextView) view;
if (mTranslationTransformation == null
|| !response.equals(mTranslationTransformation.getViewTranslationResponse())) {
TransformationMethod originalTranslationMethod =
- ((TextView) view).getTransformationMethod();
+ theTextView.getTransformationMethod();
mTranslationTransformation = new TranslationTransformationMethod(response,
originalTranslationMethod);
}
final TransformationMethod transformation = mTranslationTransformation;
+ WeakReference<TextView> textViewRef = new WeakReference<>(theTextView);
runChangeTextWithAnimationIfNeeded(
- (TextView) view,
+ theTextView,
() -> {
mIsShowingTranslation = true;
mAnimationRunning = false;
- // TODO(b/178353965): well-handle setTransformationMethod.
- ((TextView) view).setTransformationMethod(transformation);
+
+ TextView textView = textViewRef.get();
+ if (textView == null) {
+ return;
+ }
+ // TODO(b/177214256): support selectable text translation.
+ // We use the TransformationMethod to implement showing the translated text. The
+ // TextView does not support the text length change for TransformationMethod.
+ // If the text is selectable or editable, it will crash while selecting the
+ // text. To support being able to select translated text, we need broader
+ // changes to text APIs. For now, the callback makes the text non-selectable
+ // while translated, and makes it selectable again after translation.
+ mOriginalIsTextSelectable = textView.isTextSelectable();
+ if (mOriginalIsTextSelectable) {
+ // According to documentation for `setTextIsSelectable()`, it sets the
+ // flags focusable, focusableInTouchMode, clickable, and longClickable
+ // to the same value. We get the original values to restore when translation
+ // is hidden.
+ mOriginalFocusableInTouchMode = textView.isFocusableInTouchMode();
+ mOriginalFocusable = textView.getFocusable();
+ mOriginalClickable = textView.isClickable();
+ mOriginalLongClickable = textView.isLongClickable();
+ textView.setTextIsSelectable(false);
+ }
+
+ // TODO(b/233406028): We should NOT restore the original
+ // TransformationMethod and selectable state if it was changed WHILE
+ // translation was being shown.
+ textView.setTransformationMethod(transformation);
});
if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) {
CharSequence translatedContentDescription =
@@ -122,12 +158,34 @@
if (mTranslationTransformation != null) {
final TransformationMethod transformation =
mTranslationTransformation.getOriginalTransformationMethod();
+ TextView theTextView = (TextView) view;
+ WeakReference<TextView> textViewRef = new WeakReference<>(theTextView);
runChangeTextWithAnimationIfNeeded(
- (TextView) view,
+ theTextView,
() -> {
mIsShowingTranslation = false;
mAnimationRunning = false;
- ((TextView) view).setTransformationMethod(transformation);
+
+ TextView textView = textViewRef.get();
+ if (textView == null) {
+ return;
+ }
+ // TODO(b/233406028): We should NOT restore the original
+ // TransformationMethod and selectable state if it was changed WHILE
+ // translation was being shown.
+ textView.setTransformationMethod(transformation);
+
+ if (mOriginalIsTextSelectable && !textView.isTextSelectable()) {
+ // According to documentation for `setTextIsSelectable()`, it sets the
+ // flags focusable, focusableInTouchMode, clickable, and longClickable
+ // to the same value, and you must call `setFocusable()`, etc. to
+ // restore all previous flag values.
+ textView.setTextIsSelectable(true);
+ textView.setFocusableInTouchMode(mOriginalFocusableInTouchMode);
+ textView.setFocusable(mOriginalFocusable);
+ textView.setClickable(mOriginalClickable);
+ textView.setLongClickable(mOriginalLongClickable);
+ }
});
if (!TextUtils.isEmpty(mContentDescription)) {
view.setContentDescription(mContentDescription);
@@ -258,6 +316,7 @@
mAnimator.setRepeatCount(1);
mAnimator.setDuration(mAnimationDurationMillis);
final ColorStateList originalColors = view.getTextColors();
+ WeakReference<TextView> viewRef = new WeakReference<>(view);
mAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
@@ -265,7 +324,10 @@
@Override
public void onAnimationEnd(Animator animation) {
- view.setTextColor(originalColors);
+ TextView view = viewRef.get();
+ if (view != null) {
+ view.setTextColor(originalColors);
+ }
mAnimator = null;
}
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index d3cc918..a5aefd5 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -126,6 +126,11 @@
ActivityInfo activityInfo, int windowFlags, int systemWindowFlags);
/**
+ * Returns {@code true} if the tasks which is on this virtual display can be showed on Recents.
+ */
+ public abstract boolean canShowTasksInRecents();
+
+ /**
* This is called when the top activity of the display is changed.
*/
public void onTopActivityChanged(ComponentName topActivity, int uid) {}
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index aab1bc3..1ec5325 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -36,9 +36,11 @@
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
+import android.text.Layout;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
@@ -102,6 +104,37 @@
private AppPredictor mAppPredictor;
private AppPredictor.Callback mAppPredictorCallback;
+ // For pinned direct share labels, if the text spans multiple lines, the TextView will consume
+ // the full width, even if the characters actually take up less than that. Measure the actual
+ // line widths and constrain the View's width based upon that so that the pin doesn't end up
+ // very far from the text.
+ private final View.OnLayoutChangeListener mPinTextSpacingListener =
+ 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) {
+ TextView textView = (TextView) v;
+ Layout layout = textView.getLayout();
+ if (layout != null) {
+ int textWidth = 0;
+ for (int line = 0; line < layout.getLineCount(); line++) {
+ textWidth = Math.max((int) Math.ceil(layout.getLineMax(line)),
+ textWidth);
+ }
+ int desiredWidth = textWidth + textView.getPaddingLeft()
+ + textView.getPaddingRight();
+ if (textView.getWidth() > desiredWidth) {
+ ViewGroup.LayoutParams params = textView.getLayoutParams();
+ params.width = desiredWidth;
+ textView.setLayoutParams(params);
+ // Need to wait until layout pass is over before requesting layout.
+ textView.post(() -> textView.requestLayout());
+ }
+ textView.removeOnLayoutChangeListener(this);
+ }
+ }
+ };
+
public ChooserListAdapter(Context context, List<Intent> payloadIntents,
Intent[] initialIntents, List<ResolveInfo> rList,
boolean filterLastUsed, ResolverListController resolverListController,
@@ -225,6 +258,7 @@
@Override
protected void onBindView(View view, TargetInfo info, int position) {
final ViewHolder holder = (ViewHolder) view.getTag();
+
if (info == null) {
holder.icon.setImageDrawable(
mContext.getDrawable(R.drawable.resolver_icon_placeholder));
@@ -274,6 +308,9 @@
holder.itemView.setBackground(holder.defaultItemViewBackground);
}
+ // Always remove the spacing listener, attach as needed to direct share targets below.
+ holder.text.removeOnLayoutChangeListener(mPinTextSpacingListener);
+
if (info instanceof MultiDisplayResolveInfo) {
// If the target is grouped show an indicator
Drawable bkg = mContext.getDrawable(R.drawable.chooser_group_background);
@@ -286,6 +323,7 @@
Drawable bkg = mContext.getDrawable(R.drawable.chooser_pinned_background);
holder.text.setPaddingRelative(bkg.getIntrinsicWidth() /* start */, 0, 0, 0);
holder.text.setBackground(bkg);
+ holder.text.addOnLayoutChangeListener(mPinTextSpacingListener);
} else {
holder.text.setBackground(null);
holder.text.setPaddingRelative(0, 0, 0, 0);
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index d72073d..777104d8 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -158,6 +158,12 @@
public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled";
/**
+ * Whether to show privacy chip for media projection.
+ */
+ public static final String PROPERTY_MEDIA_PROJECTION_INDICATORS_ENABLED =
+ "media_projection_indicators_enabled";
+
+ /**
* Whether to show old location indicator on all location accesses.
*/
public static final String PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED =
diff --git a/core/java/com/android/internal/graphics/cam/Cam.java b/core/java/com/android/internal/graphics/cam/Cam.java
index 1ac5e50..1df85c3 100644
--- a/core/java/com/android/internal/graphics/cam/Cam.java
+++ b/core/java/com/android/internal/graphics/cam/Cam.java
@@ -386,6 +386,13 @@
// Yellows are very chromatic at L = 100, and blues are very chromatic at L = 0. All the
// other hues are white at L = 100, and black at L = 0. To preserve consistency for users of
// this system, it is better to simply return white at L* > 99, and black and L* < 0.
+ if (frame == Frame.DEFAULT) {
+ // If the viewing conditions are the same as the default sRGB-like viewing conditions,
+ // skip to using HctSolver: it uses geometrical insights to find the closest in-gamut
+ // match to hue/chroma/lstar.
+ return HctSolver.solveToInt(hue, chroma, lstar);
+ }
+
if (chroma < 1.0 || Math.round(lstar) <= 0.0 || Math.round(lstar) >= 100.0) {
return CamUtils.intFromLstar(lstar);
}
diff --git a/core/java/com/android/internal/graphics/cam/CamUtils.java b/core/java/com/android/internal/graphics/cam/CamUtils.java
index 13dafdb..f541729 100644
--- a/core/java/com/android/internal/graphics/cam/CamUtils.java
+++ b/core/java/com/android/internal/graphics/cam/CamUtils.java
@@ -73,11 +73,123 @@
// used. It was derived using Schlomer's technique of transforming the xyY
// primaries to XYZ, then applying a correction to ensure mapping from sRGB
// 1, 1, 1 to the reference white point, D65.
- static final float[][] SRGB_TO_XYZ = {
- {0.41233895f, 0.35762064f, 0.18051042f},
- {0.2126f, 0.7152f, 0.0722f},
- {0.01932141f, 0.11916382f, 0.95034478f}
- };
+ static final double[][] SRGB_TO_XYZ =
+ new double[][] {
+ new double[] {0.41233895, 0.35762064, 0.18051042},
+ new double[] {0.2126, 0.7152, 0.0722},
+ new double[] {0.01932141, 0.11916382, 0.95034478},
+ };
+
+ static final double[][] XYZ_TO_SRGB =
+ new double[][] {
+ new double[] {
+ 3.2413774792388685, -1.5376652402851851, -0.49885366846268053,
+ },
+ new double[] {
+ -0.9691452513005321, 1.8758853451067872, 0.04156585616912061,
+ },
+ new double[] {
+ 0.05562093689691305, -0.20395524564742123, 1.0571799111220335,
+ },
+ };
+
+ /**
+ * The signum function.
+ *
+ * @return 1 if num > 0, -1 if num < 0, and 0 if num = 0
+ */
+ public static int signum(double num) {
+ if (num < 0) {
+ return -1;
+ } else if (num == 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Converts an L* value to an ARGB representation.
+ *
+ * @param lstar L* in L*a*b*
+ * @return ARGB representation of grayscale color with lightness matching L*
+ */
+ public static int argbFromLstar(double lstar) {
+ double fy = (lstar + 16.0) / 116.0;
+ double fz = fy;
+ double fx = fy;
+ double kappa = 24389.0 / 27.0;
+ double epsilon = 216.0 / 24389.0;
+ boolean lExceedsEpsilonKappa = lstar > 8.0;
+ double y = lExceedsEpsilonKappa ? fy * fy * fy : lstar / kappa;
+ boolean cubeExceedEpsilon = fy * fy * fy > epsilon;
+ double x = cubeExceedEpsilon ? fx * fx * fx : lstar / kappa;
+ double z = cubeExceedEpsilon ? fz * fz * fz : lstar / kappa;
+ float[] whitePoint = WHITE_POINT_D65;
+ return argbFromXyz(x * whitePoint[0], y * whitePoint[1], z * whitePoint[2]);
+ }
+
+ /** Converts a color from ARGB to XYZ. */
+ public static int argbFromXyz(double x, double y, double z) {
+ double[][] matrix = XYZ_TO_SRGB;
+ double linearR = matrix[0][0] * x + matrix[0][1] * y + matrix[0][2] * z;
+ double linearG = matrix[1][0] * x + matrix[1][1] * y + matrix[1][2] * z;
+ double linearB = matrix[2][0] * x + matrix[2][1] * y + matrix[2][2] * z;
+ int r = delinearized(linearR);
+ int g = delinearized(linearG);
+ int b = delinearized(linearB);
+ return argbFromRgb(r, g, b);
+ }
+
+ /** Converts a color from linear RGB components to ARGB format. */
+ public static int argbFromLinrgb(double[] linrgb) {
+ int r = delinearized(linrgb[0]);
+ int g = delinearized(linrgb[1]);
+ int b = delinearized(linrgb[2]);
+ return argbFromRgb(r, g, b);
+ }
+
+ /** Converts a color from linear RGB components to ARGB format. */
+ public static int argbFromLinrgbComponents(double r, double g, double b) {
+ return argbFromRgb(delinearized(r), delinearized(g), delinearized(b));
+ }
+
+ /**
+ * Delinearizes an RGB component.
+ *
+ * @param rgbComponent 0.0 <= rgb_component <= 100.0, represents linear R/G/B channel
+ * @return 0 <= output <= 255, color channel converted to regular RGB space
+ */
+ public static int delinearized(double rgbComponent) {
+ double normalized = rgbComponent / 100.0;
+ double delinearized = 0.0;
+ if (normalized <= 0.0031308) {
+ delinearized = normalized * 12.92;
+ } else {
+ delinearized = 1.055 * Math.pow(normalized, 1.0 / 2.4) - 0.055;
+ }
+ return clampInt(0, 255, (int) Math.round(delinearized * 255.0));
+ }
+
+ /**
+ * Clamps an integer between two integers.
+ *
+ * @return input when min <= input <= max, and either min or max otherwise.
+ */
+ public static int clampInt(int min, int max, int input) {
+ if (input < min) {
+ return min;
+ } else if (input > max) {
+ return max;
+ }
+
+ return input;
+ }
+
+ /** Converts a color from RGB components to ARGB format. */
+ public static int argbFromRgb(int red, int green, int blue) {
+ return (255 << 24) | ((red & 255) << 16) | ((green & 255) << 8) | (blue & 255);
+ }
static int intFromLstar(float lstar) {
if (lstar < 1) {
@@ -126,9 +238,9 @@
final float r = linearized(Color.red(argb));
final float g = linearized(Color.green(argb));
final float b = linearized(Color.blue(argb));
- float[][] matrix = SRGB_TO_XYZ;
- float y = (r * matrix[1][0]) + (g * matrix[1][1]) + (b * matrix[1][2]);
- return y;
+ double[][] matrix = SRGB_TO_XYZ;
+ double y = (r * matrix[1][0]) + (g * matrix[1][1]) + (b * matrix[1][2]);
+ return (float) y;
}
@NonNull
@@ -137,19 +249,30 @@
final float g = linearized(Color.green(argb));
final float b = linearized(Color.blue(argb));
- float[][] matrix = SRGB_TO_XYZ;
- float x = (r * matrix[0][0]) + (g * matrix[0][1]) + (b * matrix[0][2]);
- float y = (r * matrix[1][0]) + (g * matrix[1][1]) + (b * matrix[1][2]);
- float z = (r * matrix[2][0]) + (g * matrix[2][1]) + (b * matrix[2][2]);
- return new float[]{x, y, z};
+ double[][] matrix = SRGB_TO_XYZ;
+ double x = (r * matrix[0][0]) + (g * matrix[0][1]) + (b * matrix[0][2]);
+ double y = (r * matrix[1][0]) + (g * matrix[1][1]) + (b * matrix[1][2]);
+ double z = (r * matrix[2][0]) + (g * matrix[2][1]) + (b * matrix[2][2]);
+ return new float[]{(float) x, (float) y, (float) z};
}
- static float yFromLstar(float lstar) {
- float ke = 8.0f;
+ /**
+ * Converts an L* value to a Y value.
+ *
+ * <p>L* in L*a*b* and Y in XYZ measure the same quantity, luminance.
+ *
+ * <p>L* measures perceptual luminance, a linear scale. Y in XYZ measures relative luminance, a
+ * logarithmic scale.
+ *
+ * @param lstar L* in L*a*b*
+ * @return Y in XYZ
+ */
+ public static double yFromLstar(double lstar) {
+ double ke = 8.0;
if (lstar > ke) {
- return (float) Math.pow(((lstar + 16.0) / 116.0), 3) * 100f;
+ return Math.pow((lstar + 16.0) / 116.0, 3.0) * 100.0;
} else {
- return lstar / (24389f / 27f) * 100f;
+ return lstar / (24389.0 / 27.0) * 100.0;
}
}
diff --git a/core/java/com/android/internal/graphics/cam/Frame.java b/core/java/com/android/internal/graphics/cam/Frame.java
index c422ad1..0ac7cbc 100644
--- a/core/java/com/android/internal/graphics/cam/Frame.java
+++ b/core/java/com/android/internal/graphics/cam/Frame.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.util.MathUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* The frame, or viewing conditions, where a color was seen. Used, along with a color, to create a
* color appearance model representing the color.
@@ -68,15 +70,18 @@
private final float mFlRoot;
private final float mZ;
- float getAw() {
+ @VisibleForTesting
+ public float getAw() {
return mAw;
}
- float getN() {
+ @VisibleForTesting
+ public float getN() {
return mN;
}
- float getNbb() {
+ @VisibleForTesting
+ public float getNbb() {
return mNbb;
}
@@ -92,8 +97,9 @@
return mNc;
}
+ @VisibleForTesting
@NonNull
- float[] getRgbD() {
+ public float[] getRgbD() {
return mRgbD;
}
@@ -101,7 +107,9 @@
return mFl;
}
- float getFlRoot() {
+ @VisibleForTesting
+ @NonNull
+ public float getFlRoot() {
return mFlRoot;
}
@@ -167,7 +175,7 @@
5.0 * adaptingLuminance));
// Intermediate factor, ratio of background relative luminance to white relative luminance
- float n = CamUtils.yFromLstar(backgroundLstar) / whitepoint[1];
+ float n = (float) CamUtils.yFromLstar(backgroundLstar) / whitepoint[1];
// Base exponential nonlinearity
// note Schlomer 2018 has a typo and uses 1.58, the correct factor is 1.48
diff --git a/core/java/com/android/internal/graphics/cam/HctSolver.java b/core/java/com/android/internal/graphics/cam/HctSolver.java
new file mode 100644
index 0000000..d7a8691
--- /dev/null
+++ b/core/java/com/android/internal/graphics/cam/HctSolver.java
@@ -0,0 +1,721 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.cam;
+
+/**
+ * An efficient algorithm for determining the closest sRGB color to a set of HCT coordinates,
+ * based on geometrical insights for finding intersections in linear RGB, CAM16, and L*a*b*.
+ *
+ * Algorithm identified and implemented by Tianguang Zhang.
+ * Copied from //java/com/google/ux/material/libmonet/hct on May 22 2022.
+ * ColorUtils/MathUtils functions that were required were added to CamUtils.
+ */
+public class HctSolver {
+ private HctSolver() {}
+
+ // Matrix used when converting from linear RGB to CAM16.
+ static final double[][] SCALED_DISCOUNT_FROM_LINRGB =
+ new double[][] {
+ new double[] {
+ 0.001200833568784504, 0.002389694492170889, 0.0002795742885861124,
+ },
+ new double[] {
+ 0.0005891086651375999, 0.0029785502573438758, 0.0003270666104008398,
+ },
+ new double[] {
+ 0.00010146692491640572, 0.0005364214359186694, 0.0032979401770712076,
+ },
+ };
+
+ // Matrix used when converting from CAM16 to linear RGB.
+ static final double[][] LINRGB_FROM_SCALED_DISCOUNT =
+ new double[][] {
+ new double[] {
+ 1373.2198709594231, -1100.4251190754821, -7.278681089101213,
+ },
+ new double[] {
+ -271.815969077903, 559.6580465940733, -32.46047482791194,
+ },
+ new double[] {
+ 1.9622899599665666, -57.173814538844006, 308.7233197812385,
+ },
+ };
+
+ // Weights for transforming a set of linear RGB coordinates to Y in XYZ.
+ static final double[] Y_FROM_LINRGB = new double[] {0.2126, 0.7152, 0.0722};
+
+ // Lookup table for plane in XYZ's Y axis (relative luminance) that corresponds to a given
+ // L* in L*a*b*. HCT's T is L*, and XYZ's Y is directly correlated to linear RGB, this table
+ // allows us to thus find the intersection between HCT and RGB, giving a solution to the
+ // RGB coordinates that correspond to a given set of HCT coordinates.
+ static final double[] CRITICAL_PLANES =
+ new double[] {
+ 0.015176349177441876,
+ 0.045529047532325624,
+ 0.07588174588720938,
+ 0.10623444424209313,
+ 0.13658714259697685,
+ 0.16693984095186062,
+ 0.19729253930674434,
+ 0.2276452376616281,
+ 0.2579979360165119,
+ 0.28835063437139563,
+ 0.3188300904430532,
+ 0.350925934958123,
+ 0.3848314933096426,
+ 0.42057480301049466,
+ 0.458183274052838,
+ 0.4976837250274023,
+ 0.5391024159806381,
+ 0.5824650784040898,
+ 0.6277969426914107,
+ 0.6751227633498623,
+ 0.7244668422128921,
+ 0.775853049866786,
+ 0.829304845476233,
+ 0.8848452951698498,
+ 0.942497089126609,
+ 1.0022825574869039,
+ 1.0642236851973577,
+ 1.1283421258858297,
+ 1.1946592148522128,
+ 1.2631959812511864,
+ 1.3339731595349034,
+ 1.407011200216447,
+ 1.4823302800086415,
+ 1.5599503113873272,
+ 1.6398909516233677,
+ 1.7221716113234105,
+ 1.8068114625156377,
+ 1.8938294463134073,
+ 1.9832442801866852,
+ 2.075074464868551,
+ 2.1693382909216234,
+ 2.2660538449872063,
+ 2.36523901573795,
+ 2.4669114995532007,
+ 2.5710888059345764,
+ 2.6777882626779785,
+ 2.7870270208169257,
+ 2.898822059350997,
+ 3.0131901897720907,
+ 3.1301480604002863,
+ 3.2497121605402226,
+ 3.3718988244681087,
+ 3.4967242352587946,
+ 3.624204428461639,
+ 3.754355295633311,
+ 3.887192587735158,
+ 4.022731918402185,
+ 4.160988767090289,
+ 4.301978482107941,
+ 4.445716283538092,
+ 4.592217266055746,
+ 4.741496401646282,
+ 4.893568542229298,
+ 5.048448422192488,
+ 5.20615066083972,
+ 5.3666897647573375,
+ 5.5300801301023865,
+ 5.696336044816294,
+ 5.865471690767354,
+ 6.037501145825082,
+ 6.212438385869475,
+ 6.390297286737924,
+ 6.571091626112461,
+ 6.7548350853498045,
+ 6.941541251256611,
+ 7.131223617812143,
+ 7.323895587840543,
+ 7.5195704746346665,
+ 7.7182615035334345,
+ 7.919981813454504,
+ 8.124744458384042,
+ 8.332562408825165,
+ 8.543448553206703,
+ 8.757415699253682,
+ 8.974476575321063,
+ 9.194643831691977,
+ 9.417930041841839,
+ 9.644347703669503,
+ 9.873909240696694,
+ 10.106627003236781,
+ 10.342513269534024,
+ 10.58158024687427,
+ 10.8238400726681,
+ 11.069304815507364,
+ 11.317986476196008,
+ 11.569896988756009,
+ 11.825048221409341,
+ 12.083451977536606,
+ 12.345119996613247,
+ 12.610063955123938,
+ 12.878295467455942,
+ 13.149826086772048,
+ 13.42466730586372,
+ 13.702830557985108,
+ 13.984327217668513,
+ 14.269168601521828,
+ 14.55736596900856,
+ 14.848930523210871,
+ 15.143873411576273,
+ 15.44220572664832,
+ 15.743938506781891,
+ 16.04908273684337,
+ 16.35764934889634,
+ 16.66964922287304,
+ 16.985093187232053,
+ 17.30399201960269,
+ 17.62635644741625,
+ 17.95219714852476,
+ 18.281524751807332,
+ 18.614349837764564,
+ 18.95068293910138,
+ 19.290534541298456,
+ 19.633915083172692,
+ 19.98083495742689,
+ 20.331304511189067,
+ 20.685334046541502,
+ 21.042933821039977,
+ 21.404114048223256,
+ 21.76888489811322,
+ 22.137256497705877,
+ 22.50923893145328,
+ 22.884842241736916,
+ 23.264076429332462,
+ 23.6469514538663,
+ 24.033477234264016,
+ 24.42366364919083,
+ 24.817520537484558,
+ 25.21505769858089,
+ 25.61628489293138,
+ 26.021211842414342,
+ 26.429848230738664,
+ 26.842203703840827,
+ 27.258287870275353,
+ 27.678110301598522,
+ 28.10168053274597,
+ 28.529008062403893,
+ 28.96010235337422,
+ 29.39497283293396,
+ 29.83362889318845,
+ 30.276079891419332,
+ 30.722335150426627,
+ 31.172403958865512,
+ 31.62629557157785,
+ 32.08401920991837,
+ 32.54558406207592,
+ 33.010999283389665,
+ 33.4802739966603,
+ 33.953417292456834,
+ 34.430438229418264,
+ 34.911345834551085,
+ 35.39614910352207,
+ 35.88485700094671,
+ 36.37747846067349,
+ 36.87402238606382,
+ 37.37449765026789,
+ 37.87891309649659,
+ 38.38727753828926,
+ 38.89959975977785,
+ 39.41588851594697,
+ 39.93615253289054,
+ 40.460400508064545,
+ 40.98864111053629,
+ 41.520882981230194,
+ 42.05713473317016,
+ 42.597404951718396,
+ 43.141702194811224,
+ 43.6900349931913,
+ 44.24241185063697,
+ 44.798841244188324,
+ 45.35933162437017,
+ 45.92389141541209,
+ 46.49252901546552,
+ 47.065252796817916,
+ 47.64207110610409,
+ 48.22299226451468,
+ 48.808024568002054,
+ 49.3971762874833,
+ 49.9904556690408,
+ 50.587870934119984,
+ 51.189430279724725,
+ 51.79514187861014,
+ 52.40501387947288,
+ 53.0190544071392,
+ 53.637271562750364,
+ 54.259673423945976,
+ 54.88626804504493,
+ 55.517063457223934,
+ 56.15206766869424,
+ 56.79128866487574,
+ 57.43473440856916,
+ 58.08241284012621,
+ 58.734331877617365,
+ 59.39049941699807,
+ 60.05092333227251,
+ 60.715611475655585,
+ 61.38457167773311,
+ 62.057811747619894,
+ 62.7353394731159,
+ 63.417162620860914,
+ 64.10328893648692,
+ 64.79372614476921,
+ 65.48848194977529,
+ 66.18756403501224,
+ 66.89098006357258,
+ 67.59873767827808,
+ 68.31084450182222,
+ 69.02730813691093,
+ 69.74813616640164,
+ 70.47333615344107,
+ 71.20291564160104,
+ 71.93688215501312,
+ 72.67524319850172,
+ 73.41800625771542,
+ 74.16517879925733,
+ 74.9167682708136,
+ 75.67278210128072,
+ 76.43322770089146,
+ 77.1981124613393,
+ 77.96744375590167,
+ 78.74122893956174,
+ 79.51947534912904,
+ 80.30219030335869,
+ 81.08938110306934,
+ 81.88105503125999,
+ 82.67721935322541,
+ 83.4778813166706,
+ 84.28304815182372,
+ 85.09272707154808,
+ 85.90692527145302,
+ 86.72564993000343,
+ 87.54890820862819,
+ 88.3767072518277,
+ 89.2090541872801,
+ 90.04595612594655,
+ 90.88742016217518,
+ 91.73345337380438,
+ 92.58406282226491,
+ 93.43925555268066,
+ 94.29903859396902,
+ 95.16341895893969,
+ 96.03240364439274,
+ 96.9059996312159,
+ 97.78421388448044,
+ 98.6670533535366,
+ 99.55452497210776,
+ };
+
+ /**
+ * Sanitizes a small enough angle in radians.
+ *
+ * @param angle An angle in radians; must not deviate too much from 0.
+ * @return A coterminal angle between 0 and 2pi.
+ */
+ static double sanitizeRadians(double angle) {
+ return (angle + Math.PI * 8) % (Math.PI * 2);
+ }
+
+ /**
+ * Delinearizes an RGB component, returning a floating-point number.
+ *
+ * @param rgbComponent 0.0 <= rgb_component <= 100.0, represents linear R/G/B channel
+ * @return 0.0 <= output <= 255.0, color channel converted to regular RGB space
+ */
+ static double trueDelinearized(double rgbComponent) {
+ double normalized = rgbComponent / 100.0;
+ double delinearized;
+ if (normalized <= 0.0031308) {
+ delinearized = normalized * 12.92;
+ } else {
+ delinearized = 1.055 * Math.pow(normalized, 1.0 / 2.4) - 0.055;
+ }
+ return delinearized * 255.0;
+ }
+
+ static double chromaticAdaptation(double component) {
+ double af = Math.pow(Math.abs(component), 0.42);
+ return CamUtils.signum(component) * 400.0 * af / (af + 27.13);
+ }
+
+ /**
+ * Returns the hue of a linear RGB color in CAM16.
+ *
+ * @param linrgb The linear RGB coordinates of a color.
+ * @return The hue of the color in CAM16, in radians.
+ */
+ static double hueOf(double[] linrgb) {
+ // Calculate scaled discount components using in-lined matrix multiplication to avoid
+ // an array allocation.
+ double[][] matrix = SCALED_DISCOUNT_FROM_LINRGB;
+ double[] row = linrgb;
+ double rD = linrgb[0] * matrix[0][0] + row[1] * matrix[0][1] + row[2] * matrix[0][2];
+ double gD = linrgb[0] * matrix[1][0] + row[1] * matrix[1][1] + row[2] * matrix[1][2];
+ double bD = linrgb[0] * matrix[2][0] + row[1] * matrix[2][1] + row[2] * matrix[2][2];
+
+ double rA = chromaticAdaptation(rD);
+ double gA = chromaticAdaptation(gD);
+ double bA = chromaticAdaptation(bD);
+ // redness-greenness
+ double a = (11.0 * rA + -12.0 * gA + bA) / 11.0;
+ // yellowness-blueness
+ double b = (rA + gA - 2.0 * bA) / 9.0;
+ return Math.atan2(b, a);
+ }
+
+ /**
+ * Cyclic order is the idea that 330° → 5° → 200° is in order, but, 180° → 270° → 210° is not.
+ * Visually, A B and C are angles, and they are in cyclic order if travelling from A to C
+ * in a way that increases angle (ex. counter-clockwise if +x axis = 0 degrees and +y = 90)
+ * means you must cross B.
+ * @param a first angle in possibly cyclic triplet
+ * @param b second angle in possibly cyclic triplet
+ * @param c third angle in possibly cyclic triplet
+ * @return true if B is between A and C
+ */
+ static boolean areInCyclicOrder(double a, double b, double c) {
+ double deltaAB = sanitizeRadians(b - a);
+ double deltaAC = sanitizeRadians(c - a);
+ return deltaAB < deltaAC;
+ }
+
+ /**
+ * Find an intercept using linear interpolation.
+ *
+ * @param source The starting number.
+ * @param mid The number in the middle.
+ * @param target The ending number.
+ * @return A number t such that lerp(source, target, t) = mid.
+ */
+ static double intercept(double source, double mid, double target) {
+ if (target == source) {
+ return target;
+ }
+ return (mid - source) / (target - source);
+ }
+
+ /**
+ * Linearly interpolate between two points in three dimensions.
+ *
+ * @param source three dimensions representing the starting point
+ * @param t the percentage to travel between source and target, from 0 to 1
+ * @param target three dimensions representing the end point
+ * @return three dimensions representing the point t percent from source to target.
+ */
+ static double[] lerpPoint(double[] source, double t, double[] target) {
+ return new double[] {
+ source[0] + (target[0] - source[0]) * t,
+ source[1] + (target[1] - source[1]) * t,
+ source[2] + (target[2] - source[2]) * t,
+ };
+ }
+
+ /**
+ * Intersects a segment with a plane.
+ *
+ * @param source The coordinates of point A.
+ * @param coordinate The R-, G-, or B-coordinate of the plane.
+ * @param target The coordinates of point B.
+ * @param axis The axis the plane is perpendicular with. (0: R, 1: G, 2: B)
+ * @return The intersection point of the segment AB with the plane R=coordinate, G=coordinate,
+ * or B=coordinate
+ */
+ static double[] setCoordinate(double[] source, double coordinate, double[] target, int axis) {
+ double t = intercept(source[axis], coordinate, target[axis]);
+ return lerpPoint(source, t, target);
+ }
+
+ /** Ensure X is between 0 and 100. */
+ static boolean isBounded(double x) {
+ return 0.0 <= x && x <= 100.0;
+ }
+
+ /**
+ * Returns the nth possible vertex of the polygonal intersection.
+ *
+ * @param y The Y value of the plane.
+ * @param n The zero-based index of the point. 0 <= n <= 11.
+ * @return The nth possible vertex of the polygonal intersection of the y plane and the RGB cube
+ * in linear RGB coordinates, if it exists. If the possible vertex lies outside of the cube,
+ * [-1.0, -1.0, -1.0] is returned.
+ */
+ static double[] nthVertex(double y, int n) {
+ double kR = Y_FROM_LINRGB[0];
+ double kG = Y_FROM_LINRGB[1];
+ double kB = Y_FROM_LINRGB[2];
+ double coordA = n % 4 <= 1 ? 0.0 : 100.0;
+ double coordB = n % 2 == 0 ? 0.0 : 100.0;
+ if (n < 4) {
+ double g = coordA;
+ double b = coordB;
+ double r = (y - g * kG - b * kB) / kR;
+ if (isBounded(r)) {
+ return new double[] {r, g, b};
+ } else {
+ return new double[] {-1.0, -1.0, -1.0};
+ }
+ } else if (n < 8) {
+ double b = coordA;
+ double r = coordB;
+ double g = (y - r * kR - b * kB) / kG;
+ if (isBounded(g)) {
+ return new double[] {r, g, b};
+ } else {
+ return new double[] {-1.0, -1.0, -1.0};
+ }
+ } else {
+ double r = coordA;
+ double g = coordB;
+ double b = (y - r * kR - g * kG) / kB;
+ if (isBounded(b)) {
+ return new double[] {r, g, b};
+ } else {
+ return new double[] {-1.0, -1.0, -1.0};
+ }
+ }
+ }
+
+ /**
+ * Finds the segment containing the desired color.
+ *
+ * @param y The Y value of the color.
+ * @param targetHue The hue of the color.
+ * @return A list of two sets of linear RGB coordinates, each corresponding to an endpoint of
+ * the segment containing the desired color.
+ */
+ static double[][] bisectToSegment(double y, double targetHue) {
+ double[] left = new double[] {-1.0, -1.0, -1.0};
+ double[] right = left;
+ double leftHue = 0.0;
+ double rightHue = 0.0;
+ boolean initialized = false;
+ boolean uncut = true;
+ for (int n = 0; n < 12; n++) {
+ double[] mid = nthVertex(y, n);
+ if (mid[0] < 0) {
+ continue;
+ }
+ double midHue = hueOf(mid);
+ if (!initialized) {
+ left = mid;
+ right = mid;
+ leftHue = midHue;
+ rightHue = midHue;
+ initialized = true;
+ continue;
+ }
+ if (uncut || areInCyclicOrder(leftHue, midHue, rightHue)) {
+ uncut = false;
+ if (areInCyclicOrder(leftHue, targetHue, midHue)) {
+ right = mid;
+ rightHue = midHue;
+ } else {
+ left = mid;
+ leftHue = midHue;
+ }
+ }
+ }
+ return new double[][] {left, right};
+ }
+
+ static int criticalPlaneBelow(double x) {
+ return (int) Math.floor(x - 0.5);
+ }
+
+ static int criticalPlaneAbove(double x) {
+ return (int) Math.ceil(x - 0.5);
+ }
+
+ /**
+ * Finds a color with the given Y and hue on the boundary of the cube.
+ *
+ * @param y The Y value of the color.
+ * @param targetHue The hue of the color.
+ * @return The desired color, in linear RGB coordinates.
+ */
+ static int bisectToLimit(double y, double targetHue) {
+ double[][] segment = bisectToSegment(y, targetHue);
+ double[] left = segment[0];
+ double leftHue = hueOf(left);
+ double[] right = segment[1];
+ for (int axis = 0; axis < 3; axis++) {
+ if (left[axis] != right[axis]) {
+ int lPlane = -1;
+ int rPlane = 255;
+ if (left[axis] < right[axis]) {
+ lPlane = criticalPlaneBelow(trueDelinearized(left[axis]));
+ rPlane = criticalPlaneAbove(trueDelinearized(right[axis]));
+ } else {
+ lPlane = criticalPlaneAbove(trueDelinearized(left[axis]));
+ rPlane = criticalPlaneBelow(trueDelinearized(right[axis]));
+ }
+ for (int i = 0; i < 8; i++) {
+ if (Math.abs(rPlane - lPlane) <= 1) {
+ break;
+ } else {
+ int mPlane = (int) Math.floor((lPlane + rPlane) / 2.0);
+ double midPlaneCoordinate = CRITICAL_PLANES[mPlane];
+ double[] mid = setCoordinate(left, midPlaneCoordinate, right, axis);
+ double midHue = hueOf(mid);
+ if (areInCyclicOrder(leftHue, targetHue, midHue)) {
+ right = mid;
+ rPlane = mPlane;
+ } else {
+ left = mid;
+ leftHue = midHue;
+ lPlane = mPlane;
+ }
+ }
+ }
+ }
+ }
+ return CamUtils.argbFromLinrgbComponents((left[0] + right[0]) / 2,
+ (left[1] + right[1]) / 2, (left[2] + right[2]) / 2);
+ }
+
+ /** Equation used in CAM16 conversion that removes the effect of chromatic adaptation. */
+ static double inverseChromaticAdaptation(double adapted) {
+ double adaptedAbs = Math.abs(adapted);
+ double base = Math.max(0, 27.13 * adaptedAbs / (400.0 - adaptedAbs));
+ return CamUtils.signum(adapted) * Math.pow(base, 1.0 / 0.42);
+ }
+
+ /**
+ * Finds a color with the given hue, chroma, and Y.
+ *
+ * @param hueRadians The desired hue in radians.
+ * @param chroma The desired chroma.
+ * @param y The desired Y.
+ * @return The desired color as a hexadecimal integer, if found; 0 otherwise.
+ */
+ static int findResultByJ(double hueRadians, double chroma, double y) {
+ // Initial estimate of j.
+ double j = Math.sqrt(y) * 11.0;
+ // ===========================================================
+ // Operations inlined from Cam16 to avoid repeated calculation
+ // ===========================================================
+ Frame viewingConditions = Frame.DEFAULT;
+ double tInnerCoeff = 1 / Math.pow(1.64 - Math.pow(0.29, viewingConditions.getN()), 0.73);
+ double eHue = 0.25 * (Math.cos(hueRadians + 2.0) + 3.8);
+ double p1 = eHue * (50000.0 / 13.0) * viewingConditions.getNc()
+ * viewingConditions.getNcb();
+ double hSin = Math.sin(hueRadians);
+ double hCos = Math.cos(hueRadians);
+ for (int iterationRound = 0; iterationRound < 5; iterationRound++) {
+ // ===========================================================
+ // Operations inlined from Cam16 to avoid repeated calculation
+ // ===========================================================
+ double jNormalized = j / 100.0;
+ double alpha = chroma == 0.0 || j == 0.0 ? 0.0 : chroma / Math.sqrt(jNormalized);
+ double t = Math.pow(alpha * tInnerCoeff, 1.0 / 0.9);
+ double acExponent = 1.0 / viewingConditions.getC() / viewingConditions.getZ();
+ double ac = viewingConditions.getAw() * Math.pow(jNormalized, acExponent);
+ double p2 = ac / viewingConditions.getNbb();
+ double gamma = 23.0 * (p2 + 0.305) * t / (23.0 * p1 + 11 * t * hCos + 108.0 * t * hSin);
+ double a = gamma * hCos;
+ double b = gamma * hSin;
+ double rA = (460.0 * p2 + 451.0 * a + 288.0 * b) / 1403.0;
+ double gA = (460.0 * p2 - 891.0 * a - 261.0 * b) / 1403.0;
+ double bA = (460.0 * p2 - 220.0 * a - 6300.0 * b) / 1403.0;
+ double rCScaled = inverseChromaticAdaptation(rA);
+ double gCScaled = inverseChromaticAdaptation(gA);
+ double bCScaled = inverseChromaticAdaptation(bA);
+ double[][] matrix = LINRGB_FROM_SCALED_DISCOUNT;
+ double linrgbR = rCScaled * matrix[0][0] + gCScaled * matrix[0][1]
+ + bCScaled * matrix[0][2];
+ double linrgbG = rCScaled * matrix[1][0] + gCScaled * matrix[1][1]
+ + bCScaled * matrix[1][2];
+ double linrgbB = rCScaled * matrix[2][0] + gCScaled * matrix[2][1]
+ + bCScaled * matrix[2][2];
+ // ===========================================================
+ // Operations inlined from Cam16 to avoid repeated calculation
+ // ===========================================================
+ if (linrgbR < 0 || linrgbG < 0 || linrgbB < 0) {
+ return 0;
+ }
+ double kR = Y_FROM_LINRGB[0];
+ double kG = Y_FROM_LINRGB[1];
+ double kB = Y_FROM_LINRGB[2];
+ double fnj = kR * linrgbR + kG * linrgbG + kB * linrgbB;
+ if (fnj <= 0) {
+ return 0;
+ }
+ if (iterationRound == 4 || Math.abs(fnj - y) < 0.002) {
+ if (linrgbR > 100.01 || linrgbG > 100.01 || linrgbB > 100.01) {
+ return 0;
+ }
+ return CamUtils.argbFromLinrgbComponents(linrgbR, linrgbG, linrgbB);
+ }
+ // Iterates with Newton method,
+ // Using 2 * fn(j) / j as the approximation of fn'(j)
+ j = j - (fnj - y) * j / (2 * fnj);
+ }
+ return 0;
+ }
+
+ /**
+ * Finds an sRGB color with the given hue, chroma, and L*, if possible.
+ *
+ * @param hueDegrees The desired hue, in degrees.
+ * @param chroma The desired chroma.
+ * @param lstar The desired L*.
+ * @return A hexadecimal representing the sRGB color. The color has sufficiently close hue,
+ * chroma, and L* to the desired values, if possible; otherwise, the hue and L* will be
+ * sufficiently close, and chroma will be maximized.
+ */
+ public static int solveToInt(double hueDegrees, double chroma, double lstar) {
+ if (chroma < 0.0001 || lstar < 0.0001 || lstar > 99.9999) {
+ return CamUtils.argbFromLstar(lstar);
+ }
+ hueDegrees = sanitizeDegreesDouble(hueDegrees);
+ double hueRadians = Math.toRadians(hueDegrees);
+ double y = CamUtils.yFromLstar(lstar);
+ int exactAnswer = findResultByJ(hueRadians, chroma, y);
+ if (exactAnswer != 0) {
+ return exactAnswer;
+ }
+ return bisectToLimit(y, hueRadians);
+ }
+
+ /**
+ * Sanitizes a degree measure as a floating-point number.
+ *
+ * @return a degree measure between 0.0 (inclusive) and 360.0 (exclusive).
+ */
+ public static double sanitizeDegreesDouble(double degrees) {
+ degrees = degrees % 360.0;
+ if (degrees < 0) {
+ degrees = degrees + 360.0;
+ }
+ return degrees;
+ }
+
+ /**
+ * Finds an sRGB color with the given hue, chroma, and L*, if possible.
+ *
+ * @param hueDegrees The desired hue, in degrees.
+ * @param chroma The desired chroma.
+ * @param lstar The desired L*.
+ * @return An CAM16 object representing the sRGB color. The color has sufficiently close hue,
+ * chroma, and L* to the desired values, if possible; otherwise, the hue and L* will be
+ * sufficiently close, and chroma will be maximized.
+ */
+ public static Cam solveToCam(double hueDegrees, double chroma, double lstar) {
+ return Cam.fromInt(solveToInt(hueDegrees, chroma, lstar));
+ }
+}
diff --git a/core/proto/android/os/processstarttime.proto b/core/proto/android/os/processstarttime.proto
deleted file mode 100644
index d0f8bae..0000000
--- a/core/proto/android/os/processstarttime.proto
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-package android.os;
-
-option java_multiple_files = true;
-
-// This message is used for statsd logging and should be kept in sync with
-// frameworks/proto_logging/stats/atoms.proto
-/**
- * Logs information about process start time.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message ProcessStartTime {
- // The uid of the ProcessRecord.
- optional int32 uid = 1;
-
- // The process pid.
- optional int32 pid = 2;
-
- // The process name.
- // Usually package name, "system" for system server.
- // Provided by ActivityManagerService.
- optional string process_name = 3;
-
- enum StartType {
- UNKNOWN = 0;
- WARM = 1;
- HOT = 2;
- COLD = 3;
- }
-
- // The start type.
- optional StartType type = 4;
-
- // The elapsed realtime at the start of the process.
- optional int64 process_start_time_millis = 5;
-
- // Number of milliseconds it takes to reach bind application.
- optional int32 bind_application_delay_millis = 6;
-
- // Number of milliseconds it takes to finish start of the process.
- optional int32 process_start_delay_millis = 7;
-
- // hostingType field in ProcessRecord, the component type such as "activity",
- // "service", "content provider", "broadcast" or other strings.
- optional string hosting_type = 8;
-
- // hostingNameStr field in ProcessRecord. The component class name that runs
- // in this process.
- optional string hosting_name = 9;
-
- // Broadcast action name.
- optional string broadcast_action_name = 10;
-
- enum HostingTypeId {
- HOSTING_TYPE_UNKNOWN = 0;
- HOSTING_TYPE_ACTIVITY = 1;
- HOSTING_TYPE_ADDED_APPLICATION = 2;
- HOSTING_TYPE_BACKUP = 3;
- HOSTING_TYPE_BROADCAST = 4;
- HOSTING_TYPE_CONTENT_PROVIDER = 5;
- HOSTING_TYPE_LINK_FAIL = 6;
- HOSTING_TYPE_ON_HOLD = 7;
- HOSTING_TYPE_NEXT_ACTIVITY = 8;
- HOSTING_TYPE_NEXT_TOP_ACTIVITY = 9;
- HOSTING_TYPE_RESTART = 10;
- HOSTING_TYPE_SERVICE = 11;
- HOSTING_TYPE_SYSTEM = 12;
- HOSTING_TYPE_TOP_ACTIVITY = 13;
- HOSTING_TYPE_EMPTY = 14;
- }
-
- optional HostingTypeId hosting_type_id = 11;
-}
-
diff --git a/core/res/res/layout/log_access_user_consent_dialog_permission.xml b/core/res/res/layout/log_access_user_consent_dialog_permission.xml
index 1a395b9..3da14c8 100644
--- a/core/res/res/layout/log_access_user_consent_dialog_permission.xml
+++ b/core/res/res/layout/log_access_user_consent_dialog_permission.xml
@@ -16,77 +16,86 @@
** limitations under the License.
*/
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="380dp"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center"
- android:paddingLeft="24dp"
- android:paddingRight="24dp"
- android:paddingTop="24dp"
- android:paddingBottom="24dp">
-
- <ImageView
- android:id="@+id/log_access_image_view"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_marginBottom="16dp"
- android:src="@drawable/ic_doc_document"
- tools:layout_editor_absoluteX="148dp"
- tools:layout_editor_absoluteY="35dp"
- android:gravity="center" />
-
- <TextView
- android:id="@+id/log_access_dialog_title"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginBottom="32dp"
- android:text="@string/log_access_confirmation_title"
- android:textAppearance="@style/AllowLogAccess"
- android:textColor="?android:attr/textColorPrimary"
- android:gravity="center" />
-
- <TextView
- android:id="@+id/log_access_dialog_body"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginBottom="40dp"
- android:text="@string/log_access_confirmation_body"
- android:textAppearance="@style/PrimaryAllowLogAccess"
- android:textColor="?android:attr/textColorPrimary"
- android:gravity="center" />
-
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="380dp"
+ android:layout_height="match_parent"
+ android:clipToPadding="false">
<LinearLayout
- android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <Button
- android:id="@+id/log_access_dialog_allow_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/log_access_confirmation_allow"
- style="@style/PermissionGrantButtonTop"
- android:textAppearance="@style/PermissionGrantButtonTextAppearance"
- android:layout_marginBottom="5dp"
- android:layout_centerHorizontal="true"
- android:layout_alignParentTop="true"
- android:layout_alignParentBottom="true"
- android:clipToOutline="true"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:paddingTop="24dp"
+ android:paddingBottom="24dp">
+
+ <ImageView
+ android:id="@+id/log_access_image_view"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginBottom="16dp"
+ android:src="@drawable/ic_doc_document"
+ tools:layout_editor_absoluteX="148dp"
+ tools:layout_editor_absoluteY="35dp"
android:gravity="center" />
- <Button
- android:id="@+id/log_access_dialog_deny_button"
- android:layout_width="match_parent"
+ <TextView
+ android:id="@+id/log_access_dialog_title"
android:layout_height="wrap_content"
- android:text="@string/log_access_confirmation_deny"
- style="@style/PermissionGrantButtonBottom"
- android:textAppearance="@style/PermissionGrantButtonTextAppearance"
- android:layout_centerHorizontal="true"
- android:layout_alignParentTop="true"
- android:layout_alignParentBottom="true"
- android:clipToOutline="true"
+ android:layout_width="wrap_content"
+ android:layout_marginBottom="32dp"
+ android:text="@string/log_access_confirmation_title"
+ android:textAppearance="@style/AllowLogAccess"
+ android:textColor="?android:attr/textColorPrimary"
android:gravity="center" />
+
+ <TextView
+ android:id="@+id/log_access_dialog_body"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginBottom="40dp"
+ android:text="@string/log_access_confirmation_body"
+ android:textAppearance="@style/PrimaryAllowLogAccess"
+ android:textColor="?android:attr/textColorPrimary"
+ android:gravity="center" />
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/log_access_dialog_allow_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/log_access_confirmation_allow"
+ style="@style/PermissionGrantButtonTop"
+ android:textAppearance="@style/PermissionGrantButtonTextAppearance"
+ android:layout_marginBottom="5dp"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:clipToOutline="true"
+ android:gravity="center" />
+
+ <Button
+ android:id="@+id/log_access_dialog_deny_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/log_access_confirmation_deny"
+ style="@style/PermissionGrantButtonBottom"
+ android:textAppearance="@style/PermissionGrantButtonTextAppearance"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:clipToOutline="true"
+ android:gravity="center" />
+ </LinearLayout>
</LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</ScrollView>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 6ef3a0b..527b922 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -355,8 +355,8 @@
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite que o app remova atalhos da tela inicial sem a intervenção do usuário."</string>
<string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"redirecionar as chamadas efetuadas"</string>
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Permite que o app veja o número discado ao realizar uma chamada, com a opção de redirecionar a chamada para outro número ou abortá-la."</string>
- <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"atender chamadas telefônicas"</string>
- <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permite que o app atenda uma chamada recebida."</string>
+ <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"atender ligações telefônicas"</string>
+ <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permite que o app atenda uma ligação recebida."</string>
<string name="permlab_receiveSms" msgid="505961632050451881">"receber mensagens de texto (SMS)"</string>
<string name="permdesc_receiveSms" msgid="1797345626687832285">"Permite que o app receba e processe mensagens SMS. Isso significa que o app pode monitorar ou excluir mensagens enviadas para o dispositivo sem mostrá-las para você."</string>
<string name="permlab_receiveMms" msgid="4000650116674380275">"receber mensagens de texto (MMS)"</string>
@@ -420,7 +420,7 @@
<string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Permite que o app modifique os dados sobre os contatos armazenados no dispositivo Android TV. Essa permissão autoriza os apps a excluírem dados de contato."</string>
<string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Permite que o app modifique os dados sobre os contatos armazenados no smartphone. Essa permissão autoriza os apps a excluírem dados de contato."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"ler registro de chamadas"</string>
- <string name="permdesc_readCallLog" msgid="8964770895425873433">"Este app pode ler seu histórico de chamadas."</string>
+ <string name="permdesc_readCallLog" msgid="8964770895425873433">"Este app pode ler seu histórico de ligações."</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"salvar no registo de chamadas"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Permite que o app modifique o registro de chamadas de seu tablet, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usar esta permissão para apagar ou modificar seu registro de chamadas."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite que o app modifique o registro de chamadas do seu dispositivo Android TV, incluindo dados sobre chamadas recebidas e realizadas. Apps maliciosos podem usar essa permissão para apagar ou modificar seu registro de chamadas."</string>
@@ -1907,7 +1907,7 @@
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string>
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em andamento"</string>
- <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma chamada recebida"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma ligação recebida"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sem classificação"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Você definiu a importância dessas notificações."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Isso é importante por causa das pessoas envolvidas."</string>
@@ -2071,7 +2071,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desativar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso mostra ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso mostra ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender ligações telefônicas e controlar o Não perturbe."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 6ef3a0b..527b922 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -355,8 +355,8 @@
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite que o app remova atalhos da tela inicial sem a intervenção do usuário."</string>
<string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"redirecionar as chamadas efetuadas"</string>
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Permite que o app veja o número discado ao realizar uma chamada, com a opção de redirecionar a chamada para outro número ou abortá-la."</string>
- <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"atender chamadas telefônicas"</string>
- <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permite que o app atenda uma chamada recebida."</string>
+ <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"atender ligações telefônicas"</string>
+ <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permite que o app atenda uma ligação recebida."</string>
<string name="permlab_receiveSms" msgid="505961632050451881">"receber mensagens de texto (SMS)"</string>
<string name="permdesc_receiveSms" msgid="1797345626687832285">"Permite que o app receba e processe mensagens SMS. Isso significa que o app pode monitorar ou excluir mensagens enviadas para o dispositivo sem mostrá-las para você."</string>
<string name="permlab_receiveMms" msgid="4000650116674380275">"receber mensagens de texto (MMS)"</string>
@@ -420,7 +420,7 @@
<string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Permite que o app modifique os dados sobre os contatos armazenados no dispositivo Android TV. Essa permissão autoriza os apps a excluírem dados de contato."</string>
<string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Permite que o app modifique os dados sobre os contatos armazenados no smartphone. Essa permissão autoriza os apps a excluírem dados de contato."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"ler registro de chamadas"</string>
- <string name="permdesc_readCallLog" msgid="8964770895425873433">"Este app pode ler seu histórico de chamadas."</string>
+ <string name="permdesc_readCallLog" msgid="8964770895425873433">"Este app pode ler seu histórico de ligações."</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"salvar no registo de chamadas"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Permite que o app modifique o registro de chamadas de seu tablet, incluindo dados sobre chamadas recebidas e efetuadas. Apps maliciosos podem usar esta permissão para apagar ou modificar seu registro de chamadas."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite que o app modifique o registro de chamadas do seu dispositivo Android TV, incluindo dados sobre chamadas recebidas e realizadas. Apps maliciosos podem usar essa permissão para apagar ou modificar seu registro de chamadas."</string>
@@ -1907,7 +1907,7 @@
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string>
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em andamento"</string>
- <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma chamada recebida"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma ligação recebida"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sem classificação"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Você definiu a importância dessas notificações."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Isso é importante por causa das pessoas envolvidas."</string>
@@ -2071,7 +2071,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desativar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso mostra ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso mostra ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender ligações telefônicas e controlar o Não perturbe."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0844dfd..ac60235 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1872,7 +1872,7 @@
<!-- The package name of the default captive portal login app. Must be granted the
POST_NOTIFICATIONS permission.
-->
- <string name="config_defaultCaptivePortalLoginPackageName" translatable="false"></string>
+ <string name="config_defaultCaptivePortalLoginPackageName" translatable="false">com.android.captiveportallogin</string>
<!-- Whether to enable geocoder overlay which allows geocoder to be replaced
by an app at run-time. When disabled, only the
diff --git a/core/tests/coretests/src/android/colormodel/CamTest.java b/core/tests/coretests/src/android/colormodel/CamTest.java
index a70ecd72..5bcc593 100644
--- a/core/tests/coretests/src/android/colormodel/CamTest.java
+++ b/core/tests/coretests/src/android/colormodel/CamTest.java
@@ -18,6 +18,9 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.LargeTest;
+
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -69,7 +72,7 @@
public void camFromGreen() {
Cam cam = Cam.fromInt(GREEN);
assertEquals(79.331f, cam.getJ(), 0.001f);
- assertEquals(108.409f, cam.getChroma(), 0.001f);
+ assertEquals(108.410f, cam.getChroma(), 0.001f);
assertEquals(142.139f, cam.getHue(), 0.001f);
assertEquals(85.587f, cam.getM(), 0.001f);
assertEquals(78.604f, cam.getS(), 0.001f);
@@ -193,4 +196,32 @@
public void deltaERedToBlue() {
assertEquals(21.415f, Cam.fromInt(RED).distance(Cam.fromInt(BLUE)), 0.001f);
}
+
+ @Test
+ public void viewingConditions_default() {
+ Frame vc = Frame.DEFAULT;
+
+ Assert.assertEquals(0.184, vc.getN(), 0.001);
+ Assert.assertEquals(29.981, vc.getAw(), 0.001);
+ Assert.assertEquals(1.016, vc.getNbb(), 0.001);
+ Assert.assertEquals(1.021, vc.getRgbD()[0], 0.001);
+ Assert.assertEquals(0.986, vc.getRgbD()[1], 0.001);
+ Assert.assertEquals(0.933, vc.getRgbD()[2], 0.001);
+ Assert.assertEquals(0.789, vc.getFlRoot(), 0.001);
+ }
+
+ @LargeTest
+ @Test
+ public void testHctReflexivity() {
+ for (int i = 0; i <= 0x00ffffff; i++) {
+ int color = 0xFF000000 | i;
+ Cam hct = Cam.fromInt(color);
+ int reconstructedFromHct = Cam.getInt(hct.getHue(), hct.getChroma(),
+ CamUtils.lstarFromInt(color));
+
+ Assert.assertEquals("input was " + Integer.toHexString(color)
+ + "; output was " + Integer.toHexString(reconstructedFromHct),
+ reconstructedFromHct, reconstructedFromHct);
+ }
+ }
}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 61bb1ee..6897c01 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -120,6 +120,10 @@
<group gid="reserved_disk" />
</permission>
+ <permission name="android.permission.WRITE_SECURITY_LOG">
+ <group gid="security_log_writer" />
+ </permission>
+
<!-- These are permissions that were mapped to gids but we need
to keep them here until an upgrade from L to the current
version is to be supported. These permissions are built-in
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 3380a23..575c3f0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -530,11 +530,18 @@
if (container == splitContainer.getPrimaryContainer()) {
// The new launched can be in the primary container when it is starting a new activity
- // onCreate, thus the secondary may still be empty.
+ // onCreate.
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
+ final Intent secondaryIntent = secondaryContainer.getPendingAppearedIntent();
+ if (secondaryIntent != null) {
+ // Check with the pending Intent before it is started on the server side.
+ // This can happen if the launched Activity start a new Intent to secondary during
+ // #onCreated().
+ return getSplitRule(launchedActivity, secondaryIntent) != null;
+ }
final Activity secondaryActivity = secondaryContainer.getTopNonFinishingActivity();
- return secondaryActivity == null
- || getSplitRule(launchedActivity, secondaryActivity) != null;
+ return secondaryActivity != null
+ && getSplitRule(launchedActivity, secondaryActivity) != null;
}
// Check if the new launched activity is a placeholder.
@@ -573,7 +580,7 @@
Activity activityBelow = null;
final TaskFragmentContainer container = getContainerWithActivity(activity);
if (container != null) {
- final List<Activity> containerActivities = container.collectActivities();
+ final List<Activity> containerActivities = container.collectNonFinishingActivities();
final int index = containerActivities.indexOf(activity);
if (index > 0) {
activityBelow = containerActivities.get(index - 1);
@@ -691,7 +698,7 @@
// 1. Whether the new activity intent should always expand.
if (shouldExpand(null /* activity */, intent)) {
- return createEmptyExpandedContainer(wct, taskId, launchingActivity);
+ return createEmptyExpandedContainer(wct, intent, taskId, launchingActivity);
}
// 2. Whether the launching activity (if set) should be split with the new activity intent.
@@ -742,7 +749,7 @@
*/
@Nullable
private TaskFragmentContainer createEmptyExpandedContainer(
- @NonNull WindowContainerTransaction wct, int taskId,
+ @NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@Nullable Activity launchingActivity) {
// We need an activity in the organizer process in the same Task to use as the owner
// activity, as well as to get the Task window info.
@@ -759,8 +766,8 @@
// Can't find any activity in the Task that we can use as the owner activity.
return null;
}
- final TaskFragmentContainer expandedContainer = newContainer(null /* activity */,
- activityInTask, taskId);
+ final TaskFragmentContainer expandedContainer = newContainer(intent, activityInTask,
+ taskId);
mPresenter.createTaskFragment(wct, expandedContainer.getTaskFragmentToken(),
activityInTask.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED);
return expandedContainer;
@@ -789,7 +796,8 @@
return splitContainer.getSecondaryContainer();
}
// Create a new TaskFragment to split with the primary activity for the new activity.
- return mPresenter.createNewSplitWithEmptySideContainer(wct, primaryActivity, splitRule);
+ return mPresenter.createNewSplitWithEmptySideContainer(wct, primaryActivity, intent,
+ splitRule);
}
/**
@@ -813,21 +821,34 @@
return null;
}
- TaskFragmentContainer newContainer(@NonNull Activity activity, int taskId) {
- return newContainer(activity, activity, taskId);
+ TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity, int taskId) {
+ return newContainer(pendingAppearedActivity, pendingAppearedActivity, taskId);
+ }
+
+ TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
+ @NonNull Activity activityInTask, int taskId) {
+ return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
+ activityInTask, taskId);
+ }
+
+ TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
+ @NonNull Activity activityInTask, int taskId) {
+ return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
+ activityInTask, taskId);
}
/**
* Creates and registers a new organized container with an optional activity that will be
* re-parented to it in a WCT.
*
- * @param activity the activity that will be reparented to the TaskFragment.
- * @param activityInTask activity in the same Task so that we can get the Task bounds if
- * needed.
- * @param taskId parent Task of the new TaskFragment.
+ * @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
+ * @param pendingAppearedIntent the Intent that will be started in the TaskFragment.
+ * @param activityInTask activity in the same Task so that we can get the Task bounds
+ * if needed.
+ * @param taskId parent Task of the new TaskFragment.
*/
- TaskFragmentContainer newContainer(@Nullable Activity activity,
- @NonNull Activity activityInTask, int taskId) {
+ TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
+ @Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId) {
if (activityInTask == null) {
throw new IllegalArgumentException("activityInTask must not be null,");
}
@@ -835,8 +856,8 @@
mTaskContainers.put(taskId, new TaskContainer(taskId));
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
- final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskContainer,
- this);
+ final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
+ pendingAppearedIntent, taskContainer, this);
if (!taskContainer.isTaskBoundsInitialized()) {
// Get the initial bounds before the TaskFragment has appeared.
final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 43d0402..ac3b05a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -101,7 +101,7 @@
@NonNull
TaskFragmentContainer createNewSplitWithEmptySideContainer(
@NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity,
- @NonNull SplitPairRule rule) {
+ @NonNull Intent secondaryIntent, @NonNull SplitPairRule rule) {
final Rect parentBounds = getParentContainerBounds(primaryActivity);
final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
isLtr(primaryActivity, rule));
@@ -111,7 +111,7 @@
// Create new empty task fragment
final int taskId = primaryContainer.getTaskId();
final TaskFragmentContainer secondaryContainer = mController.newContainer(
- null /* activity */, primaryActivity, taskId);
+ secondaryIntent, primaryActivity, taskId);
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
rule, isLtr(primaryActivity, rule));
final int windowingMode = mController.getTaskContainer(taskId)
@@ -224,7 +224,7 @@
}
final int taskId = primaryContainer.getTaskId();
- TaskFragmentContainer secondaryContainer = mController.newContainer(null /* activity */,
+ final TaskFragmentContainer secondaryContainer = mController.newContainer(activityIntent,
launchingActivity, taskId);
final int windowingMode = mController.getTaskContainer(taskId)
.getWindowingModeForSplitTaskFragment(primaryRectBounds);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 26ddae4..624cde5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.WindowConfiguration.WindowingMode;
+import android.content.Intent;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -64,7 +65,16 @@
* Activities that are being reparented or being started to this container, but haven't been
* added to {@link #mInfo} yet.
*/
- private final ArrayList<Activity> mPendingAppearedActivities = new ArrayList<>();
+ @VisibleForTesting
+ final ArrayList<Activity> mPendingAppearedActivities = new ArrayList<>();
+
+ /**
+ * When this container is created for an {@link Intent} to start within, we store that Intent
+ * until the container becomes non-empty on the server side, so that we can use it to check
+ * rules associated with this container.
+ */
+ @Nullable
+ private Intent mPendingAppearedIntent;
/** Containers that are dependent on this one and should be completely destroyed on exit. */
private final List<TaskFragmentContainer> mContainersToFinishOnExit =
@@ -99,15 +109,22 @@
* Creates a container with an existing activity that will be re-parented to it in a window
* container transaction.
*/
- TaskFragmentContainer(@Nullable Activity activity, @NonNull TaskContainer taskContainer,
+ TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
+ @Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller) {
+ if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
+ || (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
+ throw new IllegalArgumentException(
+ "One and only one of pending activity and intent must be non-null");
+ }
mController = controller;
mToken = new Binder("TaskFragmentContainer");
mTaskContainer = taskContainer;
taskContainer.mContainers.add(this);
- if (activity != null) {
- addPendingAppearedActivity(activity);
+ if (pendingAppearedActivity != null) {
+ addPendingAppearedActivity(pendingAppearedActivity);
}
+ mPendingAppearedIntent = pendingAppearedIntent;
}
/**
@@ -118,9 +135,9 @@
return mToken;
}
- /** List of activities that belong to this container and live in this process. */
+ /** List of non-finishing activities that belong to this container and live in this process. */
@NonNull
- List<Activity> collectActivities() {
+ List<Activity> collectNonFinishingActivities() {
final List<Activity> allActivities = new ArrayList<>();
if (mInfo != null) {
// Add activities reported from the server.
@@ -154,13 +171,14 @@
return false;
}
return mPendingAppearedActivities.isEmpty()
- && mInfo.getActivities().size() == collectActivities().size();
+ && mInfo.getActivities().size() == collectNonFinishingActivities().size();
}
ActivityStack toActivityStack() {
- return new ActivityStack(collectActivities(), isEmpty());
+ return new ActivityStack(collectNonFinishingActivities(), isEmpty());
}
+ /** Adds the activity that will be reparented to this container. */
void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
if (hasActivity(pendingAppearedActivity.getActivityToken())) {
return;
@@ -174,6 +192,11 @@
mPendingAppearedActivities.remove(pendingAppearedActivity);
}
+ @Nullable
+ Intent getPendingAppearedIntent() {
+ return mPendingAppearedIntent;
+ }
+
boolean hasActivity(@NonNull IBinder token) {
if (mInfo != null && mInfo.getActivities().contains(token)) {
return true;
@@ -219,7 +242,12 @@
}
mInfo = info;
- if (mInfo == null || mPendingAppearedActivities.isEmpty()) {
+ if (mInfo == null || mInfo.isEmpty()) {
+ return;
+ }
+ // Only track the pending Intent when the container is empty.
+ mPendingAppearedIntent = null;
+ if (mPendingAppearedActivities.isEmpty()) {
return;
}
// Cleanup activities that were being re-parented
@@ -234,20 +262,13 @@
@Nullable
Activity getTopNonFinishingActivity() {
- List<Activity> activities = collectActivities();
- if (activities.isEmpty()) {
- return null;
- }
- int i = activities.size() - 1;
- while (i >= 0 && activities.get(i).isFinishing()) {
- i--;
- }
- return i >= 0 ? activities.get(i) : null;
+ final List<Activity> activities = collectNonFinishingActivities();
+ return activities.isEmpty() ? null : activities.get(activities.size() - 1);
}
@Nullable
Activity getBottomMostActivity() {
- final List<Activity> activities = collectActivities();
+ final List<Activity> activities = collectNonFinishingActivities();
return activities.isEmpty() ? null : activities.get(0);
}
@@ -259,6 +280,9 @@
* Adds a container that should be finished when this container is finished.
*/
void addContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToFinish) {
+ if (mIsFinished) {
+ return;
+ }
mContainersToFinishOnExit.add(containerToFinish);
}
@@ -266,6 +290,9 @@
* Removes a container that should be finished when this container is finished.
*/
void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) {
+ if (mIsFinished) {
+ return;
+ }
mContainersToFinishOnExit.remove(containerToRemove);
}
@@ -273,6 +300,9 @@
* Adds an activity that should be finished when this container is finished.
*/
void addActivityToFinishOnExit(@NonNull Activity activityToFinish) {
+ if (mIsFinished) {
+ return;
+ }
mActivitiesToFinishOnExit.add(activityToFinish);
}
@@ -280,11 +310,17 @@
* Removes an activity that should be finished when this container is finished.
*/
void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) {
+ if (mIsFinished) {
+ return;
+ }
mActivitiesToFinishOnExit.remove(activityToRemove);
}
/** Removes all dependencies that should be finished when this container is finished. */
void resetDependencies() {
+ if (mIsFinished) {
+ return;
+ }
mContainersToFinishOnExit.clear();
mActivitiesToFinishOnExit.clear();
}
@@ -320,8 +356,11 @@
private void finishActivities(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
// Finish own activities
- for (Activity activity : collectActivities()) {
- if (!activity.isFinishing()) {
+ for (Activity activity : collectNonFinishingActivities()) {
+ if (!activity.isFinishing()
+ // In case we have requested to reparent the activity to another container (as
+ // pendingAppeared), we don't want to finish it with this container.
+ && mController.getContainerWithActivity(activity) == this) {
activity.finish();
}
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 792a531..a191e68 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Handler;
@@ -115,7 +116,7 @@
public void testExpandTaskFragment() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- taskContainer, mSplitController);
+ new Intent(), taskContainer, mSplitController);
final TaskFragmentInfo info = createMockInfo(container);
mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
container.setInfo(info);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 2fd4913..60390eb 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -19,6 +19,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
+import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -89,6 +92,10 @@
private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
new ComponentName("test", "placeholder"));
+ /** Default finish behavior in Jetpack. */
+ private static final int DEFAULT_FINISH_PRIMARY_WITH_SECONDARY = FINISH_NEVER;
+ private static final int DEFAULT_FINISH_SECONDARY_WITH_PRIMARY = FINISH_ALWAYS;
+
private Activity mActivity;
@Mock
private Resources mActivityResources;
@@ -123,7 +130,7 @@
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
// tf1 has no running activity so is not active.
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
- taskContainer, mSplitController);
+ new Intent(), taskContainer, mSplitController);
// tf2 has running activity so is active.
final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
doReturn(1).when(tf2).getRunningActivityCount();
@@ -205,7 +212,8 @@
assertThrows(IllegalArgumentException.class, () ->
mSplitController.newContainer(mActivity, null /* launchingActivity */, TASK_ID));
- final TaskFragmentContainer tf = mSplitController.newContainer(null, mActivity, TASK_ID);
+ final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, mActivity,
+ TASK_ID);
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
assertNotNull(tf);
@@ -307,7 +315,7 @@
@Test
public void testOnActivityReparentToTask_diffProcess() {
// Create an empty TaskFragment to initialize for the Task.
- mSplitController.newContainer(null, mActivity, TASK_ID);
+ mSplitController.newContainer(new Intent(), mActivity, TASK_ID);
final IBinder activityToken = new Binder();
final Intent intent = new Intent();
@@ -417,7 +425,7 @@
verify(mSplitPresenter, never()).applyTransaction(any());
- mSplitController.newContainer(null /* activity */, mActivity, TASK_ID);
+ mSplitController.newContainer(new Intent(), mActivity, TASK_ID);
mSplitController.placeActivityInTopContainer(mActivity);
verify(mSplitPresenter).applyTransaction(any());
@@ -436,7 +444,7 @@
false /* isOnReparent */);
assertFalse(result);
- verify(mSplitController, never()).newContainer(any(), any(), anyInt());
+ verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
}
@Test
@@ -577,7 +585,7 @@
final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
TASK_ID);
final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
- null /* activity */, mActivity, TASK_ID);
+ secondaryIntent, mActivity, TASK_ID);
mSplitController.registerSplit(
mTransaction,
primaryContainer,
@@ -589,11 +597,36 @@
false /* isOnReparent */);
assertTrue(result);
- verify(mSplitController, never()).newContainer(any(), any(), anyInt());
+ verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any());
}
@Test
+ public void testResolveActivityToContainer_splitRule_inPrimarySplitWithNoRuleMatched() {
+ final Intent secondaryIntent = new Intent();
+ setupSplitRule(mActivity, secondaryIntent);
+ final SplitPairRule splitRule = (SplitPairRule) mSplitController.getSplitRules().get(0);
+
+ // The new launched activity is in primary split, but there is no rule for it to split with
+ // the secondary, so return false.
+ final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
+ TASK_ID);
+ final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
+ secondaryIntent, mActivity, TASK_ID);
+ mSplitController.registerSplit(
+ mTransaction,
+ primaryContainer,
+ mActivity,
+ secondaryContainer,
+ splitRule);
+ final Activity launchedActivity = createMockActivity();
+ primaryContainer.addPendingAppearedActivity(launchedActivity);
+
+ assertFalse(mSplitController.resolveActivityToContainer(launchedActivity,
+ false /* isOnReparent */));
+ }
+
+ @Test
public void testResolveActivityToContainer_splitRule_inSecondarySplitWithRuleMatched() {
final Activity primaryActivity = createMockActivity();
setupSplitRule(primaryActivity, mActivity);
@@ -605,7 +638,7 @@
false /* isOnReparent */);
assertTrue(result);
- verify(mSplitController, never()).newContainer(any(), any(), anyInt());
+ verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any());
}
@@ -762,6 +795,38 @@
assertTrue(activityOptions.getAvoidMoveToFront());
}
+ @Test
+ public void testFinishTwoSplitThatShouldFinishTogether() {
+ // Setup two split pairs that should finish each other when finishing one.
+ final Activity secondaryActivity0 = createMockActivity();
+ final Activity secondaryActivity1 = createMockActivity();
+ final TaskFragmentContainer primaryContainer = createMockTaskFragmentContainer(mActivity);
+ final TaskFragmentContainer secondaryContainer0 = createMockTaskFragmentContainer(
+ secondaryActivity0);
+ final TaskFragmentContainer secondaryContainer1 = createMockTaskFragmentContainer(
+ secondaryActivity1);
+ final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
+ final SplitRule rule0 = createSplitRule(mActivity, secondaryActivity0, FINISH_ALWAYS,
+ FINISH_ALWAYS, false /* clearTop */);
+ final SplitRule rule1 = createSplitRule(mActivity, secondaryActivity1, FINISH_ALWAYS,
+ FINISH_ALWAYS, false /* clearTop */);
+ registerSplitPair(primaryContainer, secondaryContainer0, rule0);
+ registerSplitPair(primaryContainer, secondaryContainer1, rule1);
+
+ primaryContainer.finish(true /* shouldFinishDependent */, mSplitPresenter,
+ mTransaction, mSplitController);
+
+ // All containers and activities should be finished based on the FINISH_ALWAYS behavior.
+ assertTrue(primaryContainer.isFinished());
+ assertTrue(secondaryContainer0.isFinished());
+ assertTrue(secondaryContainer1.isFinished());
+ verify(mActivity).finish();
+ verify(secondaryActivity0).finish();
+ verify(secondaryActivity1).finish();
+ assertTrue(taskContainer.mContainers.isEmpty());
+ assertTrue(taskContainer.mSplitContainers.isEmpty());
+ }
+
/** Creates a mock activity in the organizer process. */
private Activity createMockActivity() {
final Activity activity = mock(Activity.class);
@@ -837,7 +902,9 @@
/** Setups a rule to always split the given activities. */
private void setupSplitRule(@NonNull Activity primaryActivity,
@NonNull Activity secondaryActivity) {
- final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity);
+ final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity,
+ DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
+ true /* clearTop */);
mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
}
@@ -857,29 +924,44 @@
/** Creates a rule to always split the given activities. */
private SplitRule createSplitRule(@NonNull Activity primaryActivity,
@NonNull Activity secondaryActivity) {
+ return createSplitRule(primaryActivity, secondaryActivity,
+ DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
+ true /* clearTop */);
+ }
+
+ /** Creates a rule to always split the given activities with the given finish behaviors. */
+ private SplitRule createSplitRule(@NonNull Activity primaryActivity,
+ @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
+ int finishSecondaryWithPrimary, boolean clearTop) {
final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
return new SplitPairRule.Builder(
targetPair::equals,
activityIntentPair -> false,
w -> true)
.setSplitRatio(SPLIT_RATIO)
- .setShouldClearTop(true)
+ .setFinishPrimaryWithSecondary(finishPrimaryWithSecondary)
+ .setFinishSecondaryWithPrimary(finishSecondaryWithPrimary)
+ .setShouldClearTop(clearTop)
.build();
}
/** Adds a pair of TaskFragments as split for the given activities. */
private void addSplitTaskFragments(@NonNull Activity primaryActivity,
@NonNull Activity secondaryActivity) {
- final TaskFragmentContainer primaryContainer = createMockTaskFragmentContainer(
- primaryActivity);
- final TaskFragmentContainer secondaryContainer = createMockTaskFragmentContainer(
- secondaryActivity);
+ registerSplitPair(createMockTaskFragmentContainer(primaryActivity),
+ createMockTaskFragmentContainer(secondaryActivity),
+ createSplitRule(primaryActivity, secondaryActivity));
+ }
+
+ /** Registers the two given TaskFragments as split pair. */
+ private void registerSplitPair(@NonNull TaskFragmentContainer primaryContainer,
+ @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule rule) {
mSplitController.registerSplit(
mock(WindowContainerTransaction.class),
primaryContainer,
- primaryActivity,
+ primaryContainer.getTopNonFinishingActivity(),
secondaryContainer,
- createSplitRule(primaryActivity, secondaryActivity));
+ rule);
// We need to set those in case we are not respecting clear top.
// TODO(b/231845476) we should always respect clearTop.
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index f1042ab..ebe202d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.mock;
import android.app.Activity;
+import android.content.Intent;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -142,7 +143,7 @@
assertTrue(taskContainer.isEmpty());
final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */,
- taskContainer, mController);
+ new Intent(), taskContainer, mController);
assertFalse(taskContainer.isEmpty());
@@ -158,11 +159,11 @@
assertNull(taskContainer.getTopTaskFragmentContainer());
final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */,
- taskContainer, mController);
+ new Intent(), taskContainer, mController);
assertEquals(tf0, taskContainer.getTopTaskFragmentContainer());
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
- taskContainer, mController);
+ new Intent(), taskContainer, mController);
assertEquals(tf1, taskContainer.getTopTaskFragmentContainer());
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 587878f..fcbd8a3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations;
@@ -29,12 +30,17 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import android.annotation.NonNull;
import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Point;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -49,6 +55,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -72,19 +79,35 @@
@Mock
private Handler mHandler;
private Activity mActivity;
+ private Intent mIntent;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
doReturn(mHandler).when(mController).getHandler();
mActivity = createMockActivity();
+ mIntent = new Intent();
+ }
+
+ @Test
+ public void testNewContainer() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+
+ // One of the activity and the intent must be non-null
+ assertThrows(IllegalArgumentException.class,
+ () -> new TaskFragmentContainer(null, null, taskContainer, mController));
+
+ // One of the activity and the intent must be null.
+ assertThrows(IllegalArgumentException.class,
+ () -> new TaskFragmentContainer(mActivity, mIntent, taskContainer, mController));
}
@Test
public void testFinish() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
- final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, taskContainer,
- mController);
+ final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
+ null /* pendingAppearedIntent */, taskContainer, mController);
+ doReturn(container).when(mController).getContainerWithActivity(mActivity);
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Only remove the activity, but not clear the reference until appeared.
@@ -113,10 +136,59 @@
}
@Test
+ public void testFinish_notFinishActivityThatIsReparenting() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
+ null /* pendingAppearedIntent */, taskContainer, mController);
+ final TaskFragmentInfo info = createMockTaskFragmentInfo(container0, mActivity);
+ container0.setInfo(info);
+ // Request to reparent the activity to a new TaskFragment.
+ final TaskFragmentContainer container1 = new TaskFragmentContainer(mActivity,
+ null /* pendingAppearedIntent */, taskContainer, mController);
+ doReturn(container1).when(mController).getContainerWithActivity(mActivity);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ // The activity is requested to be reparented, so don't finish it.
+ container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
+
+ verify(mActivity, never()).finish();
+ verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken());
+ verify(mController).removeContainer(container0);
+ }
+
+ @Test
+ public void testSetInfo() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ // Pending activity should be cleared when it has appeared on server side.
+ final TaskFragmentContainer pendingActivityContainer = new TaskFragmentContainer(mActivity,
+ null /* pendingAppearedIntent */, taskContainer, mController);
+
+ assertTrue(pendingActivityContainer.mPendingAppearedActivities.contains(mActivity));
+
+ final TaskFragmentInfo info0 = createMockTaskFragmentInfo(pendingActivityContainer,
+ mActivity);
+ pendingActivityContainer.setInfo(info0);
+
+ assertTrue(pendingActivityContainer.mPendingAppearedActivities.isEmpty());
+
+ // Pending intent should be cleared when the container becomes non-empty.
+ final TaskFragmentContainer pendingIntentContainer = new TaskFragmentContainer(
+ null /* pendingAppearedActivity */, mIntent, taskContainer, mController);
+
+ assertEquals(mIntent, pendingIntentContainer.getPendingAppearedIntent());
+
+ final TaskFragmentInfo info1 = createMockTaskFragmentInfo(pendingIntentContainer,
+ mActivity);
+ pendingIntentContainer.setInfo(info1);
+
+ assertNull(pendingIntentContainer.getPendingAppearedIntent());
+ }
+
+ @Test
public void testIsWaitingActivityAppear() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- taskContainer, mController);
+ mIntent, taskContainer, mController);
assertTrue(container.isWaitingActivityAppear());
@@ -137,7 +209,7 @@
public void testAppearEmptyTimeout() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- taskContainer, mController);
+ mIntent, taskContainer, mController);
assertNull(container.mAppearEmptyTimeout);
@@ -173,16 +245,16 @@
}
@Test
- public void testCollectActivities() {
+ public void testCollectNonFinishingActivities() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- taskContainer, mController);
- List<Activity> activities = container.collectActivities();
+ mIntent, taskContainer, mController);
+ List<Activity> activities = container.collectNonFinishingActivities();
assertTrue(activities.isEmpty());
container.addPendingAppearedActivity(mActivity);
- activities = container.collectActivities();
+ activities = container.collectNonFinishingActivities();
assertEquals(1, activities.size());
@@ -192,7 +264,7 @@
activity1.getActivityToken());
doReturn(runningActivities).when(mInfo).getActivities();
container.setInfo(mInfo);
- activities = container.collectActivities();
+ activities = container.collectNonFinishingActivities();
assertEquals(3, activities.size());
assertEquals(activity0, activities.get(0));
@@ -204,21 +276,21 @@
public void testAddPendingActivity() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- taskContainer, mController);
+ mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity);
- assertEquals(1, container.collectActivities().size());
+ assertEquals(1, container.collectNonFinishingActivities().size());
container.addPendingAppearedActivity(mActivity);
- assertEquals(1, container.collectActivities().size());
+ assertEquals(1, container.collectNonFinishingActivities().size());
}
@Test
public void testGetBottomMostActivity() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- taskContainer, mController);
+ mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity);
assertEquals(mActivity, container.getBottomMostActivity());
@@ -239,4 +311,18 @@
doReturn(activity).when(mController).getActivity(activityToken);
return activity;
}
+
+ /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
+ private TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
+ @NonNull Activity activity) {
+ return new TaskFragmentInfo(container.getTaskFragmentToken(),
+ mock(WindowContainerToken.class),
+ new Configuration(),
+ 1,
+ true /* isVisible */,
+ Collections.singletonList(activity.getActivityToken()),
+ new Point(),
+ false /* isTaskClearedForReuse */,
+ false /* isTaskFragmentClearedForPip */);
+ }
}
diff --git a/libs/WindowManager/Shell/res/values-af/strings_tv.xml b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
index 403cf3a..6187ea4 100644
--- a/libs/WindowManager/Shell/res/values-af/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Beeld-in-beeld"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Titellose program)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Maak toe"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Volskerm"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Skuif"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Vou uit"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Vou in"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Dubbeldruk "<annotation icon="home_icon">" TUIS "</annotation>" vir kontroles"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Prent-in-prent-kieslys"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Skuif links"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Skuif regs"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Skuif op"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Skuif af"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Klaar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
index 1377b23..74ce49e 100644
--- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ስዕል-ላይ-ስዕል"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ርዕስ የሌለው ፕሮግራም)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"ዝጋ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"ውሰድ"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ዘርጋ"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ሰብስብ"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" ለመቆጣጠሪያዎች "<annotation icon="home_icon">"መነሻ"</annotation>"ን ሁለቴ ይጫኑ"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"የስዕል-ላይ-ስዕል ምናሌ።"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ወደ ግራ ውሰድ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ወደ ቀኝ ውሰድ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ወደ ላይ ውሰድ"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ወደ ታች ውሰድ"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ተጠናቅቋል"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
index c6de71e..816b5b1 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"চিত্ৰৰ ভিতৰত চিত্ৰ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"বন্ধ কৰক"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীন"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"স্থানান্তৰ কৰক"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"বিস্তাৰ কৰক"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"সংকোচন কৰক"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" নিয়ন্ত্ৰণৰ বাবে "<annotation icon="home_icon">" গৃহপৃষ্ঠা "</annotation>" বুটামত দুবাৰ হেঁচক"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"চিত্ৰৰ ভিতৰৰ চিত্ৰ মেনু।"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"বাওঁফাললৈ নিয়ক"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"সোঁফাললৈ নিয়ক"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ওপৰলৈ নিয়ক"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"তললৈ নিয়ক"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"হ’ল"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings_tv.xml b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
index 48fc979..ccb7a70 100644
--- a/libs/WindowManager/Shell/res/values-az/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Şəkil-içində-Şəkil"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıqsız proqram)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Bağlayın"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Köçürün"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Genişləndirin"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Yığcamlaşdırın"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Nizamlayıcılar üçün "<annotation icon="home_icon">" ƏSAS SƏHİFƏ "</annotation>" süçimini iki dəfə basın"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Şəkildə şəkil menyusu."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sola köçürün"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sağa köçürün"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Yuxarı köçürün"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Aşağı köçürün"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Hazırdır"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
index bf3aa92..187d41c 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pantalla en pantalla"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sin título de programa)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Cerrar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Expandir"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Presiona dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de Pantalla en pantalla"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover hacia la izquierda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover hacia la derecha"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover hacia arriba"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover hacia abajo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Listo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
index 7590942..03f51d0 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"تصویر در تصویر"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(برنامه بدون عنوان)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"بستن"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"تمام صفحه"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"انتقال"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"گسترده کردن"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"جمع کردن"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" برای کنترلها، دکمه "<annotation icon="home_icon">"صفحه اصلی"</annotation>" را دوبار فشار دهید"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"منوی تصویر در تصویر."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"انتقال بهچپ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"انتقال بهراست"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"انتقال بهبالا"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"انتقال بهپایین"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"تمام"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
index e21962b..6c1b9db 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ચિત્રમાં-ચિત્ર"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(કોઈ ટાઇટલ પ્રોગ્રામ નથી)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"બંધ કરો"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"પૂર્ણ સ્ક્રીન"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"ખસેડો"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"મોટું કરો"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"નાનું કરો"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" નિયંત્રણો માટે "<annotation icon="home_icon">" હોમ "</annotation>" બટન પર બે વાર દબાવો"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ચિત્રમાં ચિત્ર મેનૂ."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ડાબે ખસેડો"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"જમણે ખસેડો"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ઉપર ખસેડો"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"નીચે ખસેડો"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"થઈ ગયું"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
index 7dc4532..5e065c2 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Kép a képben"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Cím nélküli program)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Bezárás"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Teljes képernyő"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Áthelyezés"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Kibontás"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Összecsukás"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Vezérlők: "<annotation icon="home_icon">" KEZDŐKÉPERNYŐ "</annotation>" gomb kétszer megnyomva"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Kép a képben menü."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mozgatás balra"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mozgatás jobbra"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mozgatás felfelé"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mozgatás lefelé"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Kész"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings_tv.xml b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
index c18f180..a48516f 100644
--- a/libs/WindowManager/Shell/res/values-it/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture in picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma senza titolo)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Chiudi"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Schermo intero"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Sposta"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Espandi"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Comprimi"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Premi due volte "<annotation icon="home_icon">" HOME "</annotation>" per aprire i controlli"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu Picture in picture."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sposta a sinistra"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sposta a destra"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Sposta su"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Sposta giù"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Fine"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index d8af43e..2af1896 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"תמונה בתוך תמונה"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"סגירה"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"העברה"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"הרחבה"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"כיווץ"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" לחיצה כפולה על "<annotation icon="home_icon">" הלחצן הראשי "</annotation>" תציג את אמצעי הבקרה"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"תפריט \'תמונה בתוך תמונה\'."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"הזזה שמאלה"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"הזזה ימינה"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"הזזה למעלה"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"הזזה למטה"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"סיום"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
index 84775438..bc7dcb7 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ピクチャー イン ピクチャー"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無題の番組)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"閉じる"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全画面表示"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"移動"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"開く"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"閉じる"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" コントロールにアクセス: "<annotation icon="home_icon">" ホーム "</annotation>" を 2 回押します"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ピクチャー イン ピクチャーのメニューです。"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"左に移動"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"右に移動"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"上に移動"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"下に移動"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"完了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
index 5a90f47..898dac2 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ეკრანი ეკრანში"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(პროგრამის სათაურის გარეშე)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"დახურვა"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"სრულ ეკრანზე"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"გადაადგილება"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"გაშლა"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ჩაკეცვა"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" მართვის საშუალებებზე წვდომისთვის ორმაგად დააჭირეთ "<annotation icon="home_icon">" მთავარ ღილაკს "</annotation></string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"მენიუ „ეკრანი ეკრანში“."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"მარცხნივ გადატანა"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"მარჯვნივ გადატანა"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ზემოთ გადატანა"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ქვემოთ გადატანა"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"მზადაა"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
index 50de324..cdf564f 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Суреттегі сурет"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Атаусыз бағдарлама)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Жабу"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Толық экран"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Жылжыту"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Жаю"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Жию"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Басқару элементтері: "<annotation icon="home_icon">" НЕГІЗГІ ЭКРАН "</annotation>" түймесін екі рет басыңыз."</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"\"Сурет ішіндегі сурет\" мәзірі."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Солға жылжыту"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Оңға жылжыту"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Жоғары жылжыту"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Төмен жылжыту"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Дайын"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
index 81dd1ea..52017dc 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Vaizdas vaizde"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa be pavadinimo)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Uždaryti"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Visas ekranas"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Perkelti"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Išskleisti"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Sutraukti"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Jei reikia valdiklių, dukart paspauskite "<annotation icon="home_icon">"PAGRINDINIS"</annotation></string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Vaizdo vaizde meniu."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Perkelti kairėn"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Perkelti dešinėn"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Perkelti aukštyn"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Perkelti žemyn"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Atlikta"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
index a49d6ab..2129322 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Слика во слика"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без наслов)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Затвори"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цел екран"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Премести"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Прошири"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Собери"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Притиснете двапати на "<annotation icon="home_icon">" HOME "</annotation>" за контроли"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Мени за „Слика во слика“."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Премести налево"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Премести надесно"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Премести нагоре"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Премести надолу"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Готово"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
index 555fa97..549e39b 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ചിത്രത്തിനുള്ളിൽ ചിത്രം"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"അടയ്ക്കുക"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്ണ്ണ സ്ക്രീന്"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"നീക്കുക"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"വികസിപ്പിക്കുക"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ചുരുക്കുക"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" നിയന്ത്രണങ്ങൾക്കായി "<annotation icon="home_icon">" ഹോം "</annotation>" രണ്ട് തവണ അമർത്തുക"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ചിത്രത്തിനുള്ളിൽ ചിത്രം മെനു."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ഇടത്തേക്ക് നീക്കുക"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"വലത്തേക്ക് നീക്കുക"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"മുകളിലേക്ക് നീക്കുക"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"താഴേക്ക് നീക്കുക"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"പൂർത്തിയായി"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
index 759cd54..9a85d96 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Дэлгэц доторх дэлгэц"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Гарчиггүй хөтөлбөр)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Хаах"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Бүтэн дэлгэц"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Зөөх"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Дэлгэх"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Хураах"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Хяналтад хандах бол "<annotation icon="home_icon">" HOME "</annotation>" дээр хоёр дарна уу"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Дэлгэцэн доторх дэлгэцийн цэс."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Зүүн тийш зөөх"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Баруун тийш зөөх"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Дээш зөөх"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Доош зөөх"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Болсон"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
index bf7899d..a9779b3 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"चित्रात-चित्र"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षक नसलेला कार्यक्रम)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"बंद करा"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रीन"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"हलवा"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"विस्तार करा"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"कोलॅप्स करा"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" नियंत्रणांसाठी "<annotation icon="home_icon">" होम "</annotation>" दोनदा दाबा"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"चित्रात-चित्र मेनू."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"डावीकडे हलवा"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"उजवीकडे हलवा"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"वर हलवा"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"खाली हलवा"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"पूर्ण झाले"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
index a9c6587..8fe992d 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Gambar dalam Gambar"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tiada tajuk)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Tutup"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Skrin penuh"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Alih"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Kembangkan"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Kuncupkan"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Tekan dua kali "<annotation icon="home_icon">" LAMAN UTAMA "</annotation>" untuk mengakses kawalan"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu Gambar dalam Gambar."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Alih ke kiri"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Alih ke kanan"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Alih ke atas"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Alih ke bawah"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Selesai"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
index ded650e..7cbf9e2 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षकविहीन कार्यक्रम)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"बन्द गर्नुहोस्"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रिन"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"सार्नुहोस्"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"एक्स्पान्ड गर्नुहोस्"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"कोल्याप्स गर्नुहोस्"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" कन्ट्रोल मेनु खोल्न "<annotation icon="home_icon">" होम "</annotation>" बटन दुई पटक थिच्नुहोस्"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"\"picture-in-picture\" मेनु।"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"बायाँतिर सार्नुहोस्"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"दायाँतिर सार्नुहोस्"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"माथितिर सार्नुहोस्"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"तलतिर सार्नुहोस्"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"सम्पन्न भयो"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
index 4d2cef6..2deaedd 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Scherm-in-scherm"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Naamloos programma)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Sluiten"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Volledig scherm"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Verplaatsen"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Uitvouwen"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Samenvouwen"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Druk twee keer op "<annotation icon="home_icon">" HOME "</annotation>" voor bedieningselementen"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Scherm-in-scherm-menu."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Naar links verplaatsen"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Naar rechts verplaatsen"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Omhoog verplaatsen"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Omlaag verplaatsen"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Klaar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
index d7094df..a1edde7 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ਸਿਰਲੇਖ-ਰਹਿਤ ਪ੍ਰੋਗਰਾਮ)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"ਬੰਦ ਕਰੋ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"ਲਿਜਾਓ"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"ਵਿਸਤਾਰ ਕਰੋ"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"ਸਮੇਟੋ"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" ਕੰਟਰੋਲਾਂ ਲਈ "<annotation icon="home_icon">" ਹੋਮ ਬਟਨ "</annotation>" ਨੂੰ ਦੋ ਵਾਰ ਦਬਾਓ"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ ਮੀਨੂ।"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ਖੱਬੇ ਲਿਜਾਓ"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"ਸੱਜੇ ਲਿਜਾਓ"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ਉੱਪਰ ਲਿਜਾਓ"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"ਹੇਠਾਂ ਲਿਜਾਓ"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ਹੋ ਗਿਆ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
index ce378a5..14d1c34 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Fechar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Abrir"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Fechar"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Pressione o botão "<annotation icon="home_icon">"home"</annotation>" duas vezes para acessar os controles"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu do picture-in-picture"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover para cima"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover para baixo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Concluído"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
index ef387c3..1ada450 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Ecrã no ecrã"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sem título do programa)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Fechar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ecrã inteiro"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Expandir"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Reduzir"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Prima duas vezes "<annotation icon="home_icon">" PÁGINA INICIAL "</annotation>" para controlos"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu de ecrã no ecrã."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover para cima"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover para baixo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Concluído"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
index ce378a5..14d1c34 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Fechar"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Mover"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Abrir"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Fechar"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Pressione o botão "<annotation icon="home_icon">"home"</annotation>" duas vezes para acessar os controles"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu do picture-in-picture"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover para a esquerda"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mover para a direita"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mover para cima"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mover para baixo"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Concluído"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
index a03809d..56dadb2 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program fără titlu)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Închideți"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ecran complet"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Mutați"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Extindeți"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Restrângeți"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Apăsați de două ori "<annotation icon="home_icon">"butonul ecran de pornire"</annotation>" pentru comenzi"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meniu picture-in-picture."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mutați spre stânga"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mutați spre dreapta"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mutați în sus"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mutați în jos"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gata"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
index db249ce..e7f55ec 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Картинка в картинке"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Без названия)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Закрыть"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Во весь экран"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Переместить"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Развернуть"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Свернуть"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Элементы управления: дважды нажмите "<annotation icon="home_icon">" кнопку главного экрана "</annotation></string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Меню \"Картинка в картинке\"."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Переместить влево"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Переместить вправо"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Переместить вверх"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Переместить вниз"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Готово"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings_tv.xml b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
index 30f049a..5478ce5 100644
--- a/libs/WindowManager/Shell/res/values-si/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"පින්තූරය-තුළ-පින්තූරය"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(මාතෘකාවක් නැති වැඩසටහන)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"වසන්න"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"සම්පූර්ණ තිරය"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"ගෙන යන්න"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"දිග හරින්න"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"හකුළන්න"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" පාලන සඳහා "<annotation icon="home_icon">" මුල් පිටුව "</annotation>" දෙවරක් ඔබන්න"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"පින්තූරය තුළ පින්තූරය මෙනුව"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"වමට ගෙන යන්න"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"දකුණට ගෙන යන්න"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"ඉහළට ගෙන යන්න"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"පහළට ගෙන යන්න"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"නිමයි"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
index 1ac11e2..1df43af 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Obraz v obraze"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez názvu)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Zavrieť"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Presunúť"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Rozbaliť"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Zbaliť"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Ovládanie zobraz. dvoj. stlač. "<annotation icon="home_icon">" TLAČIDLA PLOCHY "</annotation></string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Ponuka obrazu v obraze."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Posunúť doľava"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Posunúť doprava"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Posunúť nahor"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Posunúť nadol"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Hotovo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
index eb5f405..d3a9c3d 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Bild-i-bild"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Namnlöst program)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Stäng"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Helskärm"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Flytta"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Utöka"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Komprimera"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Tryck snabbt två gånger på "<annotation icon="home_icon">" HEM "</annotation>" för kontroller"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Bild-i-bild-meny."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Flytta åt vänster"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Flytta åt höger"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Flytta uppåt"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Flytta nedåt"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Klar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
index d643ec6..7b9a310 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pachika Picha Ndani ya Picha Nyingine"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programu isiyo na jina)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Funga"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Skrini nzima"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Hamisha"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Panua"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Kunja"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Bonyeza mara mbili kitufe cha "<annotation icon="home_icon">" UKURASA WA KWANZA "</annotation>" kupata vidhibiti"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menyu ya kipengele cha kupachika picha ndani ya picha nyingine."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sogeza kushoto"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sogeza kulia"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Sogeza juu"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Sogeza chini"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Imemaliza"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
index ee65ff5..e201401 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"பிக்ச்சர்-இன்-பிக்ச்சர்"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(தலைப்பு இல்லை)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"மூடுக"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"முழுத்திரை"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"நகர்த்து"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"விரி"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"சுருக்கு"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" கட்டுப்பாடுகள்: "<annotation icon="home_icon">" முகப்பு "</annotation>" பட்டனை இருமுறை அழுத்துக"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"பிக்ச்சர்-இன்-பிக்ச்சர் மெனு."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"இடப்புறம் நகர்த்து"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"வலப்புறம் நகர்த்து"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"மேலே நகர்த்து"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"கீழே நகர்த்து"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"முடிந்தது"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings_tv.xml b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
index 89ee690..6284d90 100644
--- a/libs/WindowManager/Shell/res/values-te/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"పిక్చర్-ఇన్-పిక్చర్"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(శీర్షిక లేని ప్రోగ్రామ్)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"మూసివేయండి"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ఫుల్-స్క్రీన్"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"తరలించండి"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"విస్తరించండి"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"కుదించండి"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" కంట్రోల్స్ కోసం "<annotation icon="home_icon">" HOME "</annotation>" బటన్ రెండుసార్లు నొక్కండి"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"పిక్చర్-ఇన్-పిక్చర్ మెనూ."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"ఎడమ వైపుగా జరపండి"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"కుడి వైపుగా జరపండి"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"పైకి జరపండి"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"కిందికి జరపండి"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"పూర్తయింది"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
index ccc67ab..4cc050b 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Walang pamagat na programa)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Isara"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Ilipat"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"I-expand"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"I-collapse"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" I-double press ang "<annotation icon="home_icon">" HOME "</annotation>" para sa mga kontrol"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menu ng Picture-in-Picture."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Ilipat pakaliwa"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Ilipat pakanan"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Itaas"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Ibaba"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Tapos na"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
index b0ad67e..579d7ae 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pencere İçinde Pencere"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıksız program)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Kapat"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Taşı"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Genişlet"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Daralt"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Kontroller için "<annotation icon="home_icon">" ANA SAYFA "</annotation>"\'ya iki kez basın"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Pencere içinde pencere menüsü."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Sola taşı"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Sağa taşı"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Yukarı taşı"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Aşağı taşı"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Bitti"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
index e92842d..e838857 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"تصویر میں تصویر"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"بند کریں"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"منتقل کریں"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"پھیلائیں"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"سکیڑیں"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" کنٹرولز کے لیے "<annotation icon="home_icon">"ہوم "</annotation>" بٹن کو دو بار دبائیں"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"تصویر میں تصویر کا مینو۔"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"دائیں منتقل کریں"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"بائیں منتقل کریں"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"اوپر منتقل کریں"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"نیچے منتقل کریں"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"ہو گیا"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
index 340c08e..da95335 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Tasvir ustida tasvir"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nomsiz)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Yopish"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Butun ekran"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Boshqa joyga olish"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Yoyish"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Yopish"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Boshqaruv uchun "<annotation icon="home_icon">"ASOSIY"</annotation>" tugmani ikki marta bosing"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Tasvir ustida tasvir menyusi."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Chapga olish"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Oʻngga olish"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Tepaga olish"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Pastga olish"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Tayyor"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
index d8d2045..1f9260f 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Hình trong hình"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Không có chương trình tiêu đề)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Đóng"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Toàn màn hình"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Di chuyển"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Mở rộng"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Thu gọn"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Nhấn đúp vào nút "<annotation icon="home_icon">" MÀN HÌNH CHÍNH "</annotation>" để mở trình đơn điều khiển"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Trình đơn hình trong hình."</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Di chuyển sang trái"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Di chuyển sang phải"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Di chuyển lên"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Di chuyển xuống"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Xong"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
index 0d79f04..399d639 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"画中画"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(节目没有标题)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"关闭"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全屏"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"移动"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"展开"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"收起"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" 按两次"<annotation icon="home_icon">"主屏幕"</annotation>"按钮可查看相关控件"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"画中画菜单。"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"左移"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"右移"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"上移"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"下移"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"完成"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
index 04a4fc3..20243a9 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -19,26 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Isithombe-esithombeni"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Alukho uhlelo lwesihloko)"</string>
- <!-- no translation found for pip_close (2955969519031223530) -->
- <skip />
+ <string name="pip_close" msgid="2955969519031223530">"Vala"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Iskrini esigcwele"</string>
- <!-- no translation found for pip_move (158770205886688553) -->
- <skip />
- <!-- no translation found for pip_expand (1051966011679297308) -->
- <skip />
- <!-- no translation found for pip_collapse (3903295106641385962) -->
- <skip />
+ <string name="pip_move" msgid="158770205886688553">"Hambisa"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Nweba"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Goqa"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Chofoza kabili "<annotation icon="home_icon">" IKHAYA"</annotation>" mayelana nezilawuli"</string>
- <!-- no translation found for a11y_pip_menu_entered (5106343214776801614) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_left (6612980937817141583) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_right (1119409122645529936) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_up (98502616918621959) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_down (3858802832725159740) -->
- <skip />
- <!-- no translation found for a11y_action_pip_move_done (1486845365134416210) -->
- <skip />
+ <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Imenyu yesithombe-esithombeni"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Yisa kwesokunxele"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Yisa kwesokudla"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Khuphula"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Yehlisa"</string>
+ <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Kwenziwe"</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index a089585..b8bf1a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -706,6 +706,8 @@
* @param animate whether the pointer should animate to this position.
*/
public void setPointerPosition(float bubblePosition, boolean onLeft, boolean animate) {
+ final boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection()
+ == LAYOUT_DIRECTION_RTL;
// Pointer gets drawn in the padding
final boolean showVertically = mPositioner.showBubblesVertically();
final float paddingLeft = (showVertically && onLeft)
@@ -732,12 +734,22 @@
float pointerX;
if (showVertically) {
pointerY = bubbleCenter - (mPointerWidth / 2f);
- pointerX = onLeft
- ? -mPointerHeight + mPointerOverlap
- : getWidth() - mPaddingRight - mPointerOverlap;
+ if (!isRtl) {
+ pointerX = onLeft
+ ? -mPointerHeight + mPointerOverlap
+ : getWidth() - mPaddingRight - mPointerOverlap;
+ } else {
+ pointerX = onLeft
+ ? -(getWidth() - mPaddingLeft - mPointerOverlap)
+ : mPointerHeight - mPointerOverlap;
+ }
} else {
pointerY = mPointerOverlap;
- pointerX = bubbleCenter - (mPointerWidth / 2f);
+ if (!isRtl) {
+ pointerX = bubbleCenter - (mPointerWidth / 2f);
+ } else {
+ pointerX = -(getWidth() - mPaddingLeft - bubbleCenter) + (mPointerWidth / 2f);
+ }
}
if (animate) {
mPointerView.animate().translationX(pointerX).translationY(pointerY).start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index d357655..4eba169 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -25,15 +25,14 @@
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.app.TaskInfo;
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
+import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -197,6 +196,15 @@
}
/**
+ * Quietly cancel the animator by removing the listeners first.
+ */
+ static void quietCancel(@NonNull ValueAnimator animator) {
+ animator.removeAllUpdateListeners();
+ animator.removeAllListeners();
+ animator.cancel();
+ }
+
+ /**
* Additional callback interface for PiP animation
*/
public static class PipAnimationCallback {
@@ -257,7 +265,7 @@
mSurfaceControlTransactionFactory;
private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private @TransitionDirection int mTransitionDirection;
- protected SurfaceControl mContentOverlay;
+ protected PipContentOverlay mContentOverlay;
private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash,
@AnimationType int animationType,
@@ -335,43 +343,26 @@
return false;
}
- SurfaceControl getContentOverlay() {
- return mContentOverlay;
+ SurfaceControl getContentOverlayLeash() {
+ return mContentOverlay == null ? null : mContentOverlay.mLeash;
}
- PipTransitionAnimator<T> setUseContentOverlay(Context context) {
+ void setColorContentOverlay(Context context) {
final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
if (mContentOverlay != null) {
- // remove existing content overlay if there is any.
- tx.remove(mContentOverlay);
- tx.apply();
+ mContentOverlay.detach(tx);
}
- mContentOverlay = new SurfaceControl.Builder(new SurfaceSession())
- .setCallsite("PipAnimation")
- .setName("PipContentOverlay")
- .setColorLayer()
- .build();
- tx.show(mContentOverlay);
- tx.setLayer(mContentOverlay, Integer.MAX_VALUE);
- tx.setColor(mContentOverlay, getContentOverlayColor(context));
- tx.setAlpha(mContentOverlay, 0f);
- tx.reparent(mContentOverlay, mLeash);
- tx.apply();
- return this;
+ mContentOverlay = new PipContentOverlay.PipColorOverlay(context);
+ mContentOverlay.attach(tx, mLeash);
}
- private float[] getContentOverlayColor(Context context) {
- final TypedArray ta = context.obtainStyledAttributes(new int[] {
- android.R.attr.colorBackground });
- try {
- int colorAccent = ta.getColor(0, 0);
- return new float[] {
- Color.red(colorAccent) / 255f,
- Color.green(colorAccent) / 255f,
- Color.blue(colorAccent) / 255f };
- } finally {
- ta.recycle();
+ void setSnapshotContentOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ if (mContentOverlay != null) {
+ mContentOverlay.detach(tx);
}
+ mContentOverlay = new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint);
+ mContentOverlay.attach(tx, mLeash);
}
/**
@@ -575,7 +566,7 @@
final Rect start = getStartValue();
final Rect end = getEndValue();
if (mContentOverlay != null) {
- tx.setAlpha(mContentOverlay, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
+ mContentOverlay.onAnimationUpdate(tx, fraction);
}
if (rotatedEndRect != null) {
// Animate the bounds in a different orientation. It only happens when
@@ -680,7 +671,7 @@
.round(tx, leash, shouldApplyCornerRadius())
.shadow(tx, leash, shouldApplyShadowRadius());
// TODO(b/178632364): this is a work around for the black background when
- // entering PiP in buttion navigation mode.
+ // entering PiP in button navigation mode.
if (isInPipDirection(direction)) {
tx.setWindowCrop(leash, getStartValue());
}
@@ -704,6 +695,9 @@
} else {
getSurfaceTransactionHelper().crop(tx, leash, destBounds);
}
+ if (mContentOverlay != null) {
+ mContentOverlay.onAnimationEnd(tx, destBounds);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
new file mode 100644
index 0000000..3dea0e0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.window.TaskSnapshot;
+
+/**
+ * Represents the content overlay used during the entering PiP animation.
+ */
+public abstract class PipContentOverlay {
+ protected SurfaceControl mLeash;
+
+ /** Attaches the internal {@link #mLeash} to the given parent leash. */
+ public abstract void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash);
+
+ /** Detaches the internal {@link #mLeash} from its parent by removing itself. */
+ public void detach(SurfaceControl.Transaction tx) {
+ if (mLeash != null && mLeash.isValid()) {
+ tx.remove(mLeash);
+ tx.apply();
+ }
+ }
+
+ /**
+ * Animates the internal {@link #mLeash} by a given fraction.
+ * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
+ * call apply on this transaction, it should be applied on the caller side.
+ * @param fraction progress of the animation ranged from 0f to 1f.
+ */
+ public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction);
+
+ /**
+ * Callback when reaches the end of animation on the internal {@link #mLeash}.
+ * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
+ * call apply on this transaction, it should be applied on the caller side.
+ * @param destinationBounds {@link Rect} of the final bounds.
+ */
+ public abstract void onAnimationEnd(SurfaceControl.Transaction atomicTx,
+ Rect destinationBounds);
+
+ /** A {@link PipContentOverlay} uses solid color. */
+ public static final class PipColorOverlay extends PipContentOverlay {
+ private final Context mContext;
+
+ public PipColorOverlay(Context context) {
+ mContext = context;
+ mLeash = new SurfaceControl.Builder(new SurfaceSession())
+ .setCallsite("PipAnimation")
+ .setName(PipColorOverlay.class.getSimpleName())
+ .setColorLayer()
+ .build();
+ }
+
+ @Override
+ public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
+ tx.show(mLeash);
+ tx.setLayer(mLeash, Integer.MAX_VALUE);
+ tx.setColor(mLeash, getContentOverlayColor(mContext));
+ tx.setAlpha(mLeash, 0f);
+ tx.reparent(mLeash, parentLeash);
+ tx.apply();
+ }
+
+ @Override
+ public void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction) {
+ atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
+ }
+
+ @Override
+ public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
+ // Do nothing. Color overlay should be fully opaque by now.
+ }
+
+ private float[] getContentOverlayColor(Context context) {
+ final TypedArray ta = context.obtainStyledAttributes(new int[] {
+ android.R.attr.colorBackground });
+ try {
+ int colorAccent = ta.getColor(0, 0);
+ return new float[] {
+ Color.red(colorAccent) / 255f,
+ Color.green(colorAccent) / 255f,
+ Color.blue(colorAccent) / 255f };
+ } finally {
+ ta.recycle();
+ }
+ }
+ }
+
+ /** A {@link PipContentOverlay} uses {@link TaskSnapshot}. */
+ public static final class PipSnapshotOverlay extends PipContentOverlay {
+ private final TaskSnapshot mSnapshot;
+ private final Rect mSourceRectHint;
+
+ private float mTaskSnapshotScaleX;
+ private float mTaskSnapshotScaleY;
+
+ public PipSnapshotOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
+ mSnapshot = snapshot;
+ mSourceRectHint = new Rect(sourceRectHint);
+ mLeash = new SurfaceControl.Builder(new SurfaceSession())
+ .setCallsite("PipAnimation")
+ .setName(PipSnapshotOverlay.class.getSimpleName())
+ .build();
+ }
+
+ @Override
+ public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
+ mTaskSnapshotScaleX = (float) mSnapshot.getTaskSize().x
+ / mSnapshot.getHardwareBuffer().getWidth();
+ mTaskSnapshotScaleY = (float) mSnapshot.getTaskSize().y
+ / mSnapshot.getHardwareBuffer().getHeight();
+ tx.show(mLeash);
+ tx.setLayer(mLeash, Integer.MAX_VALUE);
+ tx.setBuffer(mLeash, mSnapshot.getHardwareBuffer());
+ // Relocate the content to parentLeash's coordinates.
+ tx.setPosition(mLeash, -mSourceRectHint.left, -mSourceRectHint.top);
+ tx.setWindowCrop(mLeash,
+ (int) (mSourceRectHint.width() * mTaskSnapshotScaleX),
+ (int) (mSourceRectHint.height() * mTaskSnapshotScaleY));
+ tx.setScale(mLeash, mTaskSnapshotScaleX, mTaskSnapshotScaleY);
+ tx.reparent(mLeash, parentLeash);
+ tx.apply();
+ }
+
+ @Override
+ public void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction) {
+ // Do nothing. Keep the snapshot till animation ends.
+ }
+
+ @Override
+ public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
+ // Work around to make sure the snapshot overlay is aligned with PiP window before
+ // the atomicTx is committed along with the final WindowContainerTransaction.
+ final SurfaceControl.Transaction nonAtomicTx = new SurfaceControl.Transaction();
+ final float scaleX = (float) destinationBounds.width()
+ / mSourceRectHint.width();
+ final float scaleY = (float) destinationBounds.height()
+ / mSourceRectHint.height();
+ final float scale = Math.max(
+ scaleX * mTaskSnapshotScaleX, scaleY * mTaskSnapshotScaleY);
+ nonAtomicTx.setScale(mLeash, scale, scale);
+ nonAtomicTx.setPosition(mLeash,
+ -scale * mSourceRectHint.left / mTaskSnapshotScaleX,
+ -scale * mSourceRectHint.top / mTaskSnapshotScaleY);
+ nonAtomicTx.apply();
+ atomicTx.remove(mLeash);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index c6e48f5..a017a26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -105,8 +105,10 @@
SurfaceControl leash, Rect sourceRectHint,
Rect sourceBounds, Rect destinationBounds, Rect insets,
boolean isInPipDirection) {
- mTmpSourceRectF.set(sourceBounds);
mTmpDestinationRect.set(sourceBounds);
+ // Similar to {@link #scale}, we want to position the surface relative to the screen
+ // coordinates so offset the bounds to 0,0
+ mTmpDestinationRect.offsetTo(0, 0);
mTmpDestinationRect.inset(insets);
// Scale by the shortest edge and offset such that the top/left of the scaled inset source
// rect aligns with the top/left of the destination bounds
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 1f07873..c05654a 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
@@ -66,6 +66,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
+import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -152,8 +153,8 @@
final int direction = animator.getTransitionDirection();
final int animationType = animator.getAnimationType();
final Rect destinationBounds = animator.getDestinationBounds();
- if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
- fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) {
+ fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(),
animator::clearContentOverlay, true /* withStartDelay*/);
}
if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS
@@ -186,8 +187,8 @@
public void onPipAnimationCancel(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
final int direction = animator.getTransitionDirection();
- if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
- fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) {
+ fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(),
animator::clearContentOverlay, true /* withStartDelay */);
}
sendOnPipTransitionCancelled(direction);
@@ -430,7 +431,7 @@
}
}
- final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
+ final Rect destinationBounds = getExitDestinationBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
@@ -456,6 +457,9 @@
wct.setBoundsChangeTransaction(mToken, tx);
}
+ // Cancel the existing animator if there is any.
+ cancelCurrentAnimator();
+
// Set the exiting state first so if there is fixed rotation later, the running animation
// won't be interrupted by alpha animation for existing PiP.
mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
@@ -485,6 +489,11 @@
});
}
+ /** Returns the bounds to restore to when exiting PIP mode. */
+ public Rect getExitDestinationBounds() {
+ return mPipBoundsState.getDisplayBounds();
+ }
+
private void exitLaunchIntoPipTask(WindowContainerTransaction wct) {
wct.startTask(mTaskInfo.launchIntoPipHostTaskId, null /* ActivityOptions */);
mTaskOrganizer.applyTransaction(wct);
@@ -795,21 +804,13 @@
"%s: Unrecognized token: %s", TAG, token);
return;
}
+
+ cancelCurrentAnimator();
onExitPipFinished(info);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mPipTransitionController.forceFinishTransition();
}
- final PipAnimationController.PipTransitionAnimator<?> animator =
- mPipAnimationController.getCurrentAnimator();
- if (animator != null) {
- if (animator.getContentOverlay() != null) {
- removeContentOverlay(animator.getContentOverlay(), animator::clearContentOverlay);
- }
- animator.removeAllUpdateListeners();
- animator.removeAllListeners();
- animator.cancel();
- }
}
@Override
@@ -1041,9 +1042,7 @@
int direction = TRANSITION_DIRECTION_NONE;
if (animator != null) {
direction = animator.getTransitionDirection();
- animator.removeAllUpdateListeners();
- animator.removeAllListeners();
- animator.cancel();
+ PipAnimationController.quietCancel(animator);
// Do notify the listeners that this was canceled
sendOnPipTransitionCancelled(direction);
sendOnPipTransitionFinished(direction);
@@ -1491,7 +1490,17 @@
if (isInPipDirection(direction)) {
// Similar to auto-enter-pip transition, we use content overlay when there is no
// source rect hint to enter PiP use bounds animation.
- if (sourceHintRect == null) animator.setUseContentOverlay(mContext);
+ if (sourceHintRect == null) {
+ animator.setColorContentOverlay(mContext);
+ } else {
+ final TaskSnapshot snapshot = PipUtils.getTaskSnapshot(
+ mTaskInfo.launchIntoPipHostTaskId, false /* isLowResolution */);
+ if (snapshot != null) {
+ // use the task snapshot during the animation, this is for
+ // launch-into-pip aka. content-pip use case.
+ animator.setSnapshotContentOverlay(snapshot, sourceHintRect);
+ }
+ }
// The destination bounds are used for the end rect of animation and the final bounds
// after animation finishes. So after the animation is started, the destination bounds
// can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout
@@ -1555,7 +1564,7 @@
*/
void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
boolean withStartDelay) {
- if (surface == null) {
+ if (surface == null || !surface.isValid()) {
return;
}
@@ -1567,10 +1576,8 @@
// set a start delay on this animation.
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Task vanished, skip fadeOutAndRemoveOverlay", TAG);
- animation.removeAllListeners();
- animation.removeAllUpdateListeners();
- animation.cancel();
- } else {
+ PipAnimationController.quietCancel(animation);
+ } else if (surface.isValid()) {
final float alpha = (float) animation.getAnimatedValue();
final SurfaceControl.Transaction transaction =
mSurfaceControlTransactionFactory.getTransaction();
@@ -1609,6 +1616,18 @@
tx.apply();
}
+ private void cancelCurrentAnimator() {
+ final PipAnimationController.PipTransitionAnimator<?> animator =
+ mPipAnimationController.getCurrentAnimator();
+ if (animator != null) {
+ if (animator.getContentOverlayLeash() != null) {
+ removeContentOverlay(animator.getContentOverlayLeash(),
+ animator::clearContentOverlay);
+ }
+ PipAnimationController.quietCancel(animator);
+ }
+ }
+
@VisibleForTesting
public void setSurfaceControlTransactionFactory(
PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 48df28e..36e7124 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -709,7 +709,7 @@
if (sourceHintRect == null) {
// We use content overlay when there is no source rect hint to enter PiP use bounds
// animation.
- animator.setUseContentOverlay(mContext);
+ animator.setColorContentOverlay(mContext);
}
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
startTransaction.setAlpha(leash, 0f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 24993c62..54f46e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -70,8 +70,8 @@
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
return;
}
- if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
- mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(),
animator::clearContentOverlay, true /* withStartDelay*/);
}
onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
@@ -82,8 +82,8 @@
public void onPipAnimationCancel(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
final int direction = animator.getTransitionDirection();
- if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
- mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(),
animator::clearContentOverlay, true /* withStartDelay */);
}
sendOnPipTransitionCancelled(animator.getTransitionDirection());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
index c6cf8b8..dc60bcf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
@@ -19,13 +19,16 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
+import android.util.Log;
import android.util.Pair;
+import android.window.TaskSnapshot;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -106,4 +109,17 @@
}
return false;
}
+
+ /** @return {@link TaskSnapshot} for a given task id. */
+ @Nullable
+ public static TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
+ if (taskId <= 0) return null;
+ try {
+ return ActivityTaskManager.getService().getTaskSnapshot(
+ taskId, isLowResolution);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get task snapshot, taskId=" + taskId, e);
+ return null;
+ }
+ }
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d5b7ada..ccc23b7 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -291,8 +291,11 @@
most of its life. When you queue an input buffer with the {@linkplain #BUFFER_FLAG_END_OF_STREAM
end-of-stream marker}, the codec transitions to the End-of-Stream sub-state. In this state the
codec no longer accepts further input buffers, but still generates output buffers until the
- end-of-stream is reached on the output. You can move back to the Flushed sub-state at any time
- while in the Executing state using {@link #flush}.
+ end-of-stream is reached on the output. For decoders, you can move back to the Flushed sub-state
+ at any time while in the Executing state using {@link #flush}.
+ <p class=note>
+ <strong>Note:</strong> Going back to Flushed state is only supported for decoders, and may not
+ work for encoders (the behavior is undefined).
<p>
Call {@link #stop} to return the codec to the Uninitialized state, whereupon it may be configured
again. When you are done using a codec, you must release it by calling {@link #release}.
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 32fff1e..3a0699e 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -456,17 +456,27 @@
/**
* A key describing the frame rate of a video format in frames/sec.
+ * <p>
* The associated value is normally an integer when the value is used by the platform,
* but video codecs also accept float configuration values.
* Specifically, {@link MediaExtractor#getTrackFormat MediaExtractor} provides an integer
* value corresponding to the frame rate information of the track if specified and non-zero.
* Otherwise, this key is not present. {@link MediaCodec#configure MediaCodec} accepts both
- * float and integer values. This represents the desired operating frame rate if the
+ * float and integer values.
+ * <p>
+ * This represents the desired operating frame rate if the
* {@link #KEY_OPERATING_RATE} is not present and {@link #KEY_PRIORITY} is {@code 0}
- * (realtime). For video encoders this value corresponds to the intended frame rate,
- * although encoders are expected
- * to support variable frame rate based on {@link MediaCodec.BufferInfo#presentationTimeUs
- * buffer timestamp}. This key is not used in the {@code MediaCodec}
+ * (realtime). Otherwise, this is just informational.
+ * <p>
+ * For video encoders this value corresponds to the intended frame rate (the rate at which
+ * the application intends to send frames to the encoder, as calculated by the buffer
+ * timestamps, and not from the actual real-time rate that the frames are sent to
+ * the encoder). Encoders use this hint for rate control, specifically for the initial
+ * frames, as encoders are expected to support variable frame rate (for rate control) based
+ * on the actual {@link MediaCodec.BufferInfo#presentationTimeUs buffer timestamps} of
+ * subsequent frames.
+ * <p>
+ * This key is not used in the {@code MediaCodec}
* {@link MediaCodec#getInputFormat input}/{@link MediaCodec#getOutputFormat output} formats,
* nor by {@link MediaMuxer#addTrack MediaMuxer}.
*/
@@ -1069,11 +1079,20 @@
/**
* A key describing the desired profile to be used by an encoder.
+ * <p>
* The associated value is an integer.
* Constants are declared in {@link MediaCodecInfo.CodecProfileLevel}.
* This key is used as a hint, and is only supported for codecs
- * that specify a profile. Note: Codecs are free to use all the available
- * coding tools at the specified profile.
+ * that specify a profile. When configuring profile, encoder configuration
+ * may fail if other parameters are not compatible with the desired
+ * profile or if the desired profile is not supported, but it may also
+ * fail silently (where the encoder ends up using a different, compatible profile.)
+ * <p class="note">
+ * <strong>Note:</strong> Codecs are free to use all the available
+ * coding tools at the specified profile, but may ultimately choose to not do so.
+ * <p class="note">
+ * <strong>Note:</strong> When configuring video encoders, profile must be
+ * set together with {@link #KEY_LEVEL level}.
*
* @see MediaCodecInfo.CodecCapabilities#profileLevels
*/
@@ -1081,12 +1100,22 @@
/**
* A key describing the desired profile to be used by an encoder.
+ * <p>
* The associated value is an integer.
* Constants are declared in {@link MediaCodecInfo.CodecProfileLevel}.
* This key is used as a further hint when specifying a desired profile,
* and is only supported for codecs that specify a level.
* <p>
* This key is ignored if the {@link #KEY_PROFILE profile} is not specified.
+ * Otherwise, the value should be a level compatible with the configured encoding
+ * parameters.
+ * <p class="note">
+ * <strong>Note:</strong> This key cannot be used to constrain the encoder's
+ * output to a maximum encoding level. Encoders are free to target a different
+ * level if the configured encoding parameters dictate it. Nevertheless,
+ * encoders shall use (and encode) a level sufficient to decode the generated
+ * bitstream, though they may exceed the (Video) Buffering Verifier limits for
+ * that encoded level.
*
* @see MediaCodecInfo.CodecCapabilities#profileLevels
*/
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index e5673a6..13f7ee6 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -2163,7 +2163,9 @@
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, mLnbLock)
&& mLnb != null) {
mLnb.setCallbackAndOwner(this, executor, cb);
- setLnb(mLnb);
+ if (mFrontendHandle != null && mFrontend != null) {
+ setLnb(mLnb);
+ }
return mLnb;
}
return null;
@@ -2197,7 +2199,9 @@
}
mLnb = newLnb;
mLnb.setCallbackAndOwner(this, executor, cb);
- setLnb(mLnb);
+ if (mFrontendHandle != null && mFrontend != null) {
+ setLnb(mLnb);
+ }
}
return mLnb;
} finally {
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 5850a81..95599bd 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -730,6 +730,7 @@
switch (c2Buffer->data().type()) {
case C2BufferData::LINEAR: {
std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+ context->mCodecNames.push_back(mNameAtCreation.c_str());
context->mBuffer = c2Buffer;
ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
@@ -769,6 +770,7 @@
} else {
if (!mGraphicOutput) {
std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+ context->mCodecNames.push_back(mNameAtCreation.c_str());
context->mLegacyBuffer = buffer;
ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
@@ -812,7 +814,6 @@
return OK;
}
-
status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
AString name;
@@ -2286,6 +2287,108 @@
return OK;
}
+static bool obtain(
+ JMediaCodecLinearBlock *context,
+ int capacity,
+ const std::vector<std::string> &names,
+ bool secure) {
+ if (secure) {
+ // Start at 1MB, which is an arbitrary starting point that can
+ // increase when needed.
+ constexpr size_t kInitialDealerCapacity = 1048576;
+ thread_local sp<MemoryDealer> sDealer = new MemoryDealer(
+ kInitialDealerCapacity, "JNI(1MB)");
+ context->mMemory = sDealer->allocate(capacity);
+ if (context->mMemory == nullptr) {
+ size_t newDealerCapacity = sDealer->getMemoryHeap()->getSize() * 2;
+ while (capacity * 2 > newDealerCapacity) {
+ newDealerCapacity *= 2;
+ }
+ ALOGI("LinearBlock.native_obtain: "
+ "Dealer capacity increasing from %zuMB to %zuMB",
+ sDealer->getMemoryHeap()->getSize() / 1048576,
+ newDealerCapacity / 1048576);
+ sDealer = new MemoryDealer(
+ newDealerCapacity,
+ AStringPrintf("JNI(%zuMB)", newDealerCapacity).c_str());
+ context->mMemory = sDealer->allocate(capacity);
+ }
+ context->mHidlMemory = hardware::fromHeap(context->mMemory->getMemory(
+ &context->mHidlMemoryOffset, &context->mHidlMemorySize));
+ } else {
+ context->mBlock = MediaCodec::FetchLinearBlock(capacity, names);
+ if (!context->mBlock) {
+ return false;
+ }
+ }
+ context->mCodecNames = names;
+ return true;
+}
+
+static void extractMemoryFromContext(
+ JMediaCodecLinearBlock *context,
+ jint offset,
+ jint size,
+ sp<hardware::HidlMemory> *memory) {
+ *memory = context->toHidlMemory();
+ if (*memory == nullptr) {
+ if (!context->mBlock) {
+ ALOGW("extractMemoryFromContext: the buffer is missing both IMemory and C2Block");
+ return;
+ }
+ ALOGD("extractMemoryFromContext: realloc & copying from C2Block to IMemory (cap=%zu)",
+ context->capacity());
+ if (!obtain(context, context->capacity(),
+ context->mCodecNames, true /* secure */)) {
+ ALOGW("extractMemoryFromContext: failed to obtain secure block");
+ return;
+ }
+ C2WriteView view = context->mBlock->map().get();
+ if (view.error() != C2_OK) {
+ ALOGW("extractMemoryFromContext: failed to map C2Block (%d)", view.error());
+ return;
+ }
+ uint8_t *memoryPtr = static_cast<uint8_t *>(context->mMemory->unsecurePointer());
+ memcpy(memoryPtr + offset, view.base() + offset, size);
+ context->mBlock.reset();
+ context->mReadWriteMapping.reset();
+ *memory = context->toHidlMemory();
+ }
+}
+
+static void extractBufferFromContext(
+ JMediaCodecLinearBlock *context,
+ jint offset,
+ jint size,
+ std::shared_ptr<C2Buffer> *buffer) {
+ *buffer = context->toC2Buffer(offset, size);
+ if (*buffer == nullptr) {
+ if (!context->mMemory) {
+ ALOGW("extractBufferFromContext: the buffer is missing both IMemory and C2Block");
+ return;
+ }
+ ALOGD("extractBufferFromContext: realloc & copying from IMemory to C2Block (cap=%zu)",
+ context->capacity());
+ if (obtain(context, context->capacity(),
+ context->mCodecNames, false /* secure */)) {
+ ALOGW("extractBufferFromContext: failed to obtain non-secure block");
+ return;
+ }
+ C2WriteView view = context->mBlock->map().get();
+ if (view.error() != C2_OK) {
+ ALOGW("extractBufferFromContext: failed to map C2Block (%d)", view.error());
+ return;
+ }
+ uint8_t *memoryPtr = static_cast<uint8_t *>(context->mMemory->unsecurePointer());
+ memcpy(view.base() + offset, memoryPtr + offset, size);
+ context->mMemory.clear();
+ context->mHidlMemory.clear();
+ context->mHidlMemorySize = 0;
+ context->mHidlMemoryOffset = 0;
+ *buffer = context->toC2Buffer(offset, size);
+ }
+}
+
static void android_media_MediaCodec_native_queueLinearBlock(
JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
jint offset, jint size, jobject cryptoInfoObj,
@@ -2314,12 +2417,10 @@
JMediaCodecLinearBlock *context =
(JMediaCodecLinearBlock *)env->GetLongField(bufferObj, gLinearBlockInfo.contextId);
if (codec->hasCryptoOrDescrambler()) {
- memory = context->toHidlMemory();
- // TODO: copy if memory is null
+ extractMemoryFromContext(context, offset, size, &memory);
offset += context->mHidlMemoryOffset;
} else {
- buffer = context->toC2Buffer(offset, size);
- // TODO: copy if buffer is null
+ extractBufferFromContext(context, offset, size, &buffer);
}
}
env->MonitorExit(lock.get());
@@ -2354,6 +2455,7 @@
flags,
tunings,
&errorDetailMsg);
+ ALOGI_IF(err != OK, "queueEncryptedLinearBlock returned err = %d", err);
} else {
if (!buffer) {
ALOGI("queueLinearBlock: no C2Buffer found");
@@ -3300,33 +3402,9 @@
hasNonSecure = true;
}
}
- if (hasSecure && !hasNonSecure) {
- constexpr size_t kInitialDealerCapacity = 1048576; // 1MB
- thread_local sp<MemoryDealer> sDealer = new MemoryDealer(
- kInitialDealerCapacity, "JNI(1MB)");
- context->mMemory = sDealer->allocate(capacity);
- if (context->mMemory == nullptr) {
- size_t newDealerCapacity = sDealer->getMemoryHeap()->getSize() * 2;
- while (capacity * 2 > newDealerCapacity) {
- newDealerCapacity *= 2;
- }
- ALOGI("LinearBlock.native_obtain: "
- "Dealer capacity increasing from %zuMB to %zuMB",
- sDealer->getMemoryHeap()->getSize() / 1048576,
- newDealerCapacity / 1048576);
- sDealer = new MemoryDealer(
- newDealerCapacity,
- AStringPrintf("JNI(%zuMB)", newDealerCapacity).c_str());
- context->mMemory = sDealer->allocate(capacity);
- }
- context->mHidlMemory = hardware::fromHeap(context->mMemory->getMemory(
- &context->mHidlMemoryOffset, &context->mHidlMemorySize));
- } else {
- context->mBlock = MediaCodec::FetchLinearBlock(capacity, names);
- if (!context->mBlock) {
- jniThrowException(env, "java/io/IOException", nullptr);
- return;
- }
+ if (!obtain(context.get(), capacity, names, (hasSecure && !hasNonSecure) /* secure */)) {
+ jniThrowException(env, "java/io/IOException", nullptr);
+ return;
}
env->CallVoidMethod(
thiz,
diff --git a/media/jni/android_media_MediaCodecLinearBlock.h b/media/jni/android_media_MediaCodecLinearBlock.h
index ae2d3a2..c753020 100644
--- a/media/jni/android_media_MediaCodecLinearBlock.h
+++ b/media/jni/android_media_MediaCodecLinearBlock.h
@@ -25,6 +25,8 @@
namespace android {
struct JMediaCodecLinearBlock {
+ std::vector<std::string> mCodecNames;
+
std::shared_ptr<C2Buffer> mBuffer;
std::shared_ptr<C2ReadView> mReadonlyMapping;
@@ -40,7 +42,7 @@
std::once_flag mCopyWarningFlag;
- std::shared_ptr<C2Buffer> toC2Buffer(size_t offset, size_t size) {
+ std::shared_ptr<C2Buffer> toC2Buffer(size_t offset, size_t size) const {
if (mBuffer) {
if (mBuffer->data().type() != C2BufferData::LINEAR) {
return nullptr;
@@ -64,12 +66,22 @@
return nullptr;
}
- sp<hardware::HidlMemory> toHidlMemory() {
+ sp<hardware::HidlMemory> toHidlMemory() const {
if (mHidlMemory) {
return mHidlMemory;
}
return nullptr;
}
+
+ size_t capacity() const {
+ if (mBlock) {
+ return mBlock->capacity();
+ }
+ if (mMemory) {
+ return mMemory->size();
+ }
+ return 0;
+ }
};
} // namespace android
diff --git a/packages/PrintSpooler/res/values-mr/strings.xml b/packages/PrintSpooler/res/values-mr/strings.xml
index e1fa390..255fbbc 100644
--- a/packages/PrintSpooler/res/values-mr/strings.xml
+++ b/packages/PrintSpooler/res/values-mr/strings.xml
@@ -82,7 +82,7 @@
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> प्रिंट करत आहे"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द करत आहे"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिंटर एरर <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
- <string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिंटरने <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> अवरोधित केले"</string>
+ <string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिंटरने <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ब्लॉक केला"</string>
<string name="cancel" msgid="4373674107267141885">"रद्द करा"</string>
<string name="restart" msgid="2472034227037808749">"रीस्टार्ट करा"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटरवर कोणतेही कनेक्शन नाही"</string>
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index e65f7de..12db901 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -55,6 +55,7 @@
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<TextView
@@ -62,6 +63,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textDirection="locale"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"/>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index 30e3973..31e8cc7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -16,6 +16,8 @@
package com.android.settingslib.collapsingtoolbar;
+import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
+
import android.app.ActionBar;
import android.os.Build;
import android.view.LayoutInflater;
@@ -82,6 +84,9 @@
mAppBarLayout = view.findViewById(R.id.app_bar);
if (mCollapsingToolbarLayout != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+ }
}
disableCollapsingToolbarLayoutScrollingBehavior();
mToolbar = view.findViewById(R.id.action_bar);
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index dbb4b50..1ead2f3 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -16,6 +16,8 @@
package com.android.settingslib.collapsingtoolbar.widget;
+import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
+
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
@@ -106,6 +108,9 @@
mAppBarLayout = findViewById(R.id.app_bar);
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+ }
if (!TextUtils.isEmpty(mToolbarTitle)) {
mCollapsingToolbarLayout.setTitle(mToolbarTitle);
}
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index 42700b3..2c1fdd4 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -53,6 +53,7 @@
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
android:ellipsize="marquee" />
<com.android.settingslib.widget.LinkTextView
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 383bf8e..86fec50 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -30,7 +30,6 @@
import android.widget.TextView;
import androidx.annotation.ColorInt;
-import androidx.annotation.Nullable;
import com.android.settingslib.utils.BuildCompatUtils;
@@ -123,14 +122,9 @@
}
@Override
- public void setOnClickListener(@Nullable OnClickListener l) {
- super.setOnClickListener(l);
- mSwitch.setOnClickListener(l);
- }
-
- @Override
public boolean performClick() {
- return mSwitch.performClick();
+ mSwitch.performClick();
+ return super.performClick();
}
/**
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
index 906ff2c..64d100a 100644
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
@@ -66,6 +66,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
@@ -80,6 +81,7 @@
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart"
+ android:hyphenationFrequency="normalFast"
android:textColor="?android:attr/textColorSecondary"/>
<TextView
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
index 8bb56ff..2a550ef 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
@@ -66,6 +66,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
@@ -80,6 +81,7 @@
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart"
+ android:hyphenationFrequency="normalFast"
android:textColor="?android:attr/textColorSecondary"/>
<TextView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index 23aa993..d4d466a 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -43,6 +43,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -57,6 +58,7 @@
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
style="@style/PreferenceSummaryTextStyle"/>
</RelativeLayout>
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index 4d6e1b7..b2a9037 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -33,5 +33,6 @@
android:clickable="false"
android:longClickable="false"
android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="@style/TextAppearance.TopIntroText"/>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
index 2c35772..ac5807d 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
@@ -41,6 +41,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -52,6 +53,7 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
android:maxLines="10"/>
</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/preference_access_point.xml b/packages/SettingsLib/res/layout/preference_access_point.xml
index 802d604..4ad9d80 100644
--- a/packages/SettingsLib/res/layout/preference_access_point.xml
+++ b/packages/SettingsLib/res/layout/preference_access_point.xml
@@ -65,6 +65,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -76,6 +77,7 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
android:maxLines="10" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
index f512f9b..cbe49cd 100644
--- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -62,6 +62,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -73,6 +74,7 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
android:maxLines="10" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/restricted_switch_preference.xml b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
index 169ae97..edea144 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_preference.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
@@ -52,6 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -62,6 +63,7 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
android:maxLines="10" />
<TextView android:id="@+id/additional_summary"
@@ -72,6 +74,7 @@
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
android:visibility="gone" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index e394b03..f8b1452 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -570,7 +570,7 @@
<string name="user_add_profile_item_title" msgid="3111051717414643029">"Ograničeni profil"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"Dodati novog korisnika?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"Ovaj uređaj možete dijeliti s drugima ako napravite dodatne korisnike. Svaki korisnik ima svoj prostor koji može prilagoditi pomoću aplikacija, pozadinske slike i slično. Korisnici također mogu prilagoditi postavke uređaja koje utiču na sve ostale korisnike, kao što je WiFi.\n\nKada dodate novog korisnika, ta osoba treba postaviti svoj prostor.\n\nSvaki korisnik može ažurirati aplikacije za sve ostale korisnike. Postavke i usluge pristupačnosti možda se neće prenijeti na novog korisnika."</string>
- <string name="user_add_user_message_short" msgid="3295959985795716166">"Kada dodate novog korisnika, ta osoba treba postaviti svoj prostor. \n\n Svaki korisnik može ažurirati aplikacije za sve ostale korisnike."</string>
+ <string name="user_add_user_message_short" msgid="3295959985795716166">"Kada dodate novog korisnika, ta osoba treba postaviti svoj prostor. \n\nSvaki korisnik može ažurirati aplikacije za sve ostale korisnike."</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"Postaviti korisnika sada?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Provjerite može li osoba uzeti uređaj i postaviti svoj prostor"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Postaviti profil sada?"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index d0a848a..8c3f1fa 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -149,7 +149,7 @@
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parear"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAREAR"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
- <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"O pareamento dá acesso a seus contatos e ao histórico de chamadas quando estiver conectado."</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"O pareamento dá acesso a seus contatos e ao histórico de ligações quando estiver conectado."</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Não foi possível parear com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Não foi possível parear com <xliff:g id="DEVICE_NAME">%1$s</xliff:g> por causa de um PIN ou senha incorretos."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Não é possível se comunicar com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index d0a848a..8c3f1fa 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -149,7 +149,7 @@
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parear"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAREAR"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
- <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"O pareamento dá acesso a seus contatos e ao histórico de chamadas quando estiver conectado."</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"O pareamento dá acesso a seus contatos e ao histórico de ligações quando estiver conectado."</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Não foi possível parear com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Não foi possível parear com <xliff:g id="DEVICE_NAME">%1$s</xliff:g> por causa de um PIN ou senha incorretos."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Não é possível se comunicar com <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index b0e904c..51588e3 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -451,7 +451,7 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Úprava farieb môže byť užitočná, keď chcete:<br/> <ol> <li>&nbsp;zobrazovať farby presnejšie;</li> <li>&nbsp;odstrániť farby, aby ste sa mohli sústrediť.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
- <string name="power_remaining_duration_only" msgid="8264199158671531431">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+ <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ešte približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1076561255466053220">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g> – závisí to od intenzity využitia"</string>
<string name="power_discharging_duration_enhanced" msgid="1800465736237672323">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g> – závisí to od intenzity využitia (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index a9da2e0..32d5b78 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1389,16 +1389,19 @@
public void switchSubDeviceContent() {
// Backup from main device
BluetoothDevice tmpDevice = mDevice;
- short tmpRssi = mRssi;
- boolean tmpJustDiscovered = mJustDiscovered;
+ final short tmpRssi = mRssi;
+ final boolean tmpJustDiscovered = mJustDiscovered;
+ final int tmpDeviceSide = mDeviceSide;
// Set main device from sub device
mDevice = mSubDevice.mDevice;
mRssi = mSubDevice.mRssi;
mJustDiscovered = mSubDevice.mJustDiscovered;
+ mDeviceSide = mSubDevice.mDeviceSide;
// Set sub device from backup
mSubDevice.mDevice = tmpDevice;
mSubDevice.mRssi = tmpRssi;
mSubDevice.mJustDiscovered = tmpJustDiscovered;
+ mSubDevice.mDeviceSide = tmpDeviceSide;
fetchActiveDevices();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index be2a55e..76c066c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -965,24 +965,24 @@
mCachedDevice.mRssi = RSSI_1;
mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
+ mCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT);
mSubCachedDevice.mRssi = RSSI_2;
mSubCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
+ mSubCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_RIGHT);
mCachedDevice.setSubDevice(mSubCachedDevice);
- assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_1);
- assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
- assertThat(mCachedDevice.mDevice).isEqualTo(mDevice);
- assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_2);
- assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
- assertThat(mSubCachedDevice.mDevice).isEqualTo(mSubDevice);
mCachedDevice.switchSubDeviceContent();
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
+ assertThat(mCachedDevice.getDeviceSide()).isEqualTo(
+ HearingAidProfile.DeviceSide.SIDE_RIGHT);
assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
+ assertThat(mSubCachedDevice.getDeviceSide()).isEqualTo(
+ HearingAidProfile.DeviceSide.SIDE_LEFT);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index a8e6075..d80a591 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -358,8 +358,10 @@
public void onProfileConnectionStateChanged_disconnected_mainDevice_subDeviceConnected_switch()
{
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+ mCachedDevice1.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.isConnected()).thenReturn(true);
+ mCachedDevice2.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_RIGHT);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
@@ -370,6 +372,10 @@
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
+ assertThat(mCachedDevice1.getDeviceSide()).isEqualTo(
+ HearingAidProfile.DeviceSide.SIDE_RIGHT);
+ assertThat(mCachedDevice2.getDeviceSide()).isEqualTo(
+ HearingAidProfile.DeviceSide.SIDE_LEFT);
verify(mCachedDevice1).refresh();
}
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 0c82022..beba0ee 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -21,8 +21,8 @@
import android.graphics.Color
import com.android.internal.graphics.ColorUtils
import com.android.internal.graphics.cam.Cam
+import com.android.internal.graphics.cam.CamUtils
import kotlin.math.absoluteValue
-import kotlin.math.max
import kotlin.math.roundToInt
const val TAG = "ColorScheme"
@@ -43,13 +43,13 @@
* second item in the pair is a hue rotation that should be applied
*/
fun getHueRotation(sourceHue: Float, hueAndRotations: List<Pair<Int, Int>>): Double {
- for (i in 0..hueAndRotations.size) {
- val previousIndex = if (i == 0) hueAndRotations.size - 1 else i - 1
- val thisHue = hueAndRotations[i].first
- val previousHue = hueAndRotations[previousIndex].first
- if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
- return ColorScheme.wrapDegreesDouble(sourceHue.toDouble() +
- hueAndRotations[previousIndex].second)
+ val sanitizedSourceHue = (if (sourceHue < 0 || sourceHue >= 360) 0 else sourceHue).toFloat()
+ for (i in 0..hueAndRotations.size - 2) {
+ val thisHue = hueAndRotations[i].first.toFloat()
+ val nextHue = hueAndRotations[i + 1].first.toFloat()
+ if (thisHue <= sanitizedSourceHue && sanitizedSourceHue < nextHue) {
+ return ColorScheme.wrapDegreesDouble(sanitizedSourceHue.toDouble() +
+ hueAndRotations[i].second)
}
}
@@ -79,7 +79,7 @@
internal class HueVibrantSecondary() : Hue {
val hueToRotations = listOf(Pair(0, 18), Pair(41, 15), Pair(61, 10), Pair(101, 12),
- Pair(131, 15), Pair(181, 18), Pair(251, 15), Pair(301, 12))
+ Pair(131, 15), Pair(181, 18), Pair(251, 15), Pair(301, 12), Pair(360, 12))
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -87,7 +87,7 @@
internal class HueVibrantTertiary() : Hue {
val hueToRotations = listOf(Pair(0, 35), Pair(41, 30), Pair(61, 20), Pair(101, 25),
- Pair(131, 30), Pair(181, 35), Pair(251, 30), Pair(301, 25))
+ Pair(131, 30), Pair(181, 35), Pair(251, 30), Pair(301, 25), Pair(360, 25))
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -95,7 +95,7 @@
internal class HueExpressiveSecondary() : Hue {
val hueToRotations = listOf(Pair(0, 45), Pair(21, 95), Pair(51, 45), Pair(121, 20),
- Pair(141, 45), Pair(191, 90), Pair(271, 45), Pair(321, 45))
+ Pair(151, 45), Pair(191, 90), Pair(271, 45), Pair(321, 45), Pair(360, 45))
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -103,7 +103,7 @@
internal class HueExpressiveTertiary() : Hue {
val hueToRotations = listOf(Pair(0, 120), Pair(21, 120), Pair(51, 20), Pair(121, 45),
- Pair(141, 20), Pair(191, 15), Pair(271, 20), Pair(321, 120))
+ Pair(151, 20), Pair(191, 15), Pair(271, 20), Pair(321, 120), Pair(360, 120))
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -111,34 +111,13 @@
internal interface Chroma {
fun get(sourceColor: Cam): Double
-
- /**
- * Given a hue, and a mapping of hues to hue rotations, find which hues in the mapping the
- * hue fall betweens, and use the hue rotation of the lower hue.
- *
- * @param sourceHue hue of source color
- * @param hueAndChromas list of pairs, where the first item in a pair is a hue, and the
- * second item in the pair is a chroma that should be applied
- */
- fun getSpecifiedChroma(sourceHue: Float, hueAndChromas: List<Pair<Int, Int>>): Double {
- for (i in 0..hueAndChromas.size) {
- val previousIndex = if (i == 0) hueAndChromas.size - 1 else i - 1
- val thisHue = hueAndChromas[i].first
- val previousHue = hueAndChromas[previousIndex].first
- if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
- return hueAndChromas[i].second.toDouble()
- }
- }
-
- // If this statement executes, something is wrong, there should have been a rotation
- // found using the arrays.
- return sourceHue.toDouble()
- }
}
-internal class ChromaMinimum(val chroma: Double) : Chroma {
+internal class ChromaMaxOut : Chroma {
override fun get(sourceColor: Cam): Double {
- return max(sourceColor.chroma.toDouble(), chroma)
+ // Intentionally high. Gamut mapping from impossible HCT to sRGB will ensure that
+ // the maximum chroma is reached, even if lower than this constant.
+ return 130.0
}
}
@@ -192,11 +171,11 @@
n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
)),
VIBRANT(CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaMinimum(48.0)),
+ a1 = TonalSpec(HueSource(), ChromaMaxOut()),
a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(12.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(14.0))
+ n1 = TonalSpec(HueSource(), ChromaConstant(8.0)),
+ n2 = TonalSpec(HueSource(), ChromaConstant(12.0))
)),
EXPRESSIVE(CoreSpec(
a1 = TonalSpec(HueAdd(240.0), ChromaConstant(40.0)),
@@ -229,7 +208,7 @@
}
class ColorScheme(
- @ColorInt seed: Int,
+ @ColorInt val seed: Int,
val darkTheme: Boolean,
val style: Style = Style.TONAL_SPOT
) {
@@ -293,12 +272,14 @@
override fun toString(): String {
return "ColorScheme {\n" +
- " neutral1: ${humanReadable(neutral1)}\n" +
- " neutral2: ${humanReadable(neutral2)}\n" +
- " accent1: ${humanReadable(accent1)}\n" +
- " accent2: ${humanReadable(accent2)}\n" +
- " accent3: ${humanReadable(accent3)}\n" +
+ " seed color: ${stringForColor(seed)}\n" +
" style: $style\n" +
+ " palettes: \n" +
+ " ${humanReadable("PRIMARY", accent1)}\n" +
+ " ${humanReadable("SECONDARY", accent2)}\n" +
+ " ${humanReadable("TERTIARY", accent3)}\n" +
+ " ${humanReadable("NEUTRAL", neutral1)}\n" +
+ " ${humanReadable("NEUTRAL VARIANT", neutral2)}\n" +
"}"
}
@@ -416,13 +397,6 @@
return seeds
}
- internal fun angleIsBetween(angle: Float, a: Int, b: Int): Boolean {
- if (a < b) {
- return a <= angle && angle <= b
- }
- return a <= angle || angle <= b
- }
-
private fun wrapDegrees(degrees: Int): Int {
return when {
degrees < 0 -> {
@@ -455,8 +429,20 @@
return 180f - ((a - b).absoluteValue - 180f).absoluteValue
}
- private fun humanReadable(colors: List<Int>): String {
- return colors.joinToString { "#" + Integer.toHexString(it) }
+ private fun stringForColor(color: Int): String {
+ val width = 4
+ val hct = Cam.fromInt(color)
+ val h = "H${hct.hue.roundToInt().toString().padEnd(width)}"
+ val c = "C${hct.chroma.roundToInt().toString().padEnd(width)}"
+ val t = "T${CamUtils.lstarFromInt(color).roundToInt().toString().padEnd(width)}"
+ val hex = Integer.toHexString(color).replaceRange(0, 2, "").uppercase()
+ return "$h$c$t = #$hex"
+ }
+
+ private fun humanReadable(paletteName: String, colors: List<Int>): String {
+ return "$paletteName\n" + colors.map {
+ stringForColor(it)
+ }.joinToString(separator = "\n") { it }
}
private fun score(cam: Cam, proportion: Double): Double {
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_media_projection.xml b/packages/SystemUI/res/drawable/privacy_item_circle_media_projection.xml
new file mode 100644
index 0000000..ac563de
--- /dev/null
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_media_projection.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@id/background"
+ android:gravity="center"
+ >
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/ongoing_appops_dialog_circle_size"
+ android:width="@dimen/ongoing_appops_dialog_circle_size"
+ />
+ <solid android:color="@color/privacy_chip_background" />
+ </shape>
+ </item>
+ <item android:id="@id/icon"
+ android:gravity="center"
+ android:width="@dimen/ongoing_appops_dialog_icon_size"
+ android:height="@dimen/ongoing_appops_dialog_icon_size"
+ android:drawable="@drawable/stat_sys_cast"
+ />
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index f79e534..7785a09 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -40,6 +40,7 @@
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:background="@null"
+ android:contentDescription="@string/media_output_dialog_accessibility_seekbar"
android:progressDrawable="@drawable/media_output_dialog_seekbar_background"
android:thumb="@null"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 6554178..eb73da4 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Klaar"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Gekopieer"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Van <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Maak gekopieerde teks toe"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Wysig gekopieerde teks"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Wysig gekopieerde prent"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Stuur na toestel in die omtrek"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 83fb0cf..e6fb5d7 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"ተከናውኗል"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"ተቀድቷል"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"ከ<xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"የተቀዳ ጽሑፍን አሰናብት"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"የተቀዳ ጽሁፍ አርትዕ ያድርጉ"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"የተቀዳ ምስል አርትዕ ያድርጉ"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"በአቅራቢያ ወዳለ መሳሪያ ይላኩ"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 46f5cb9..631d37c 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"হ’ল"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"প্ৰতিলিপি কৰা হ’ল"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g>ৰ পৰা"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"প্ৰতিলিপি কৰা পাঠ অগ্ৰাহ্য কৰক"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"প্ৰতিলিপি কৰা পাঠ সম্পাদনা কৰক"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"প্ৰতিলিপি কৰা প্ৰতিচ্ছবি সম্পাদনা কৰক"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"নিকটৱৰ্তী ডিভাইচলৈ পঠাওক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 6710d41..fb553b6 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Oldu"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopyalandı"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Mənbə: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Kopyalanmış mətni qapadın"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Kopyalanmış mətni redaktə edin"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Kopyalanmış şəkli redaktə edin"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Yaxınlıqdakı cihaza göndərin"</string>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 5977679..2d13e56 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -88,7 +88,7 @@
</string-array>
<string-array name="tile_states_color_correction">
<item msgid="2840507878437297682">"No disponible"</item>
- <item msgid="1909756493418256167">"Desactivada"</item>
+ <item msgid="1909756493418256167">"Desactivat"</item>
<item msgid="4531508423703413340">"Activada"</item>
</string-array>
<string-array name="tile_states_inversion">
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index d6bcb7f..ecbb54a 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -895,7 +895,7 @@
<string name="person_available" msgid="2318599327472755472">"Disponible"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema al leer el medidor de batería"</string>
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Presiona para obtener más información"</string>
- <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No se estableció alarma"</string>
+ <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No establecida"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de huellas dactilares"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ingresar al dispositivo"</string>
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Listo"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Se copió"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Descartar el texto copiado"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar el texto copiado"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar la imagen copiada"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar a dispositivos cercanos"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index fcc389f..28d4baf 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"تمام"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"کپی شد"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"از <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"رد شدن نوشتار کپیشده"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"ویرایش نوشتار کپیشده"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"ویرایش تصویر کپیشده"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ارسال به دستگاهی در اطراف"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index bd1fd8e..30395f7 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"થઈ ગયું"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"કૉપિ કરવામાં આવી"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g>માંથી"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"કૉપિ કરેલી ટેક્સ્ટ છોડી દો"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"કૉપિ કરેલી ટેક્સ્ટમાં ફેરફાર કરો"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"કૉપિ કરેલી છબીમાં ફેરફાર કરો"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"નજીકના ડિવાઇસને મોકલો"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 7a54f5f..40afd6f 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Kész"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Másolva"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Forrás: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Vágólapra másolt szöveg elvetése"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Vágólapra másolt szöveg szerkesztése"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Vágólapra másolt kép szerkesztése"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Küldés közeli eszközre"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 85d3808..353762c 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Fine"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiato"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Da <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Ignora il testo copiato"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Modifica testo copiato"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Modifica immagine copiata"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Invia a dispositivo nelle vicinanze"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index ef07f17..5aab763 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -949,8 +949,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"סיום"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"הועתק"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"המקור: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"מחיקה של הטקסט שהועתק"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"עריכת הטקסט שהועתק"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"עריכת התמונה שהועתקה"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"שליחה למכשיר בקרבת מקום"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a5096f9..fc2ddef 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"完了"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"コピーしました"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> から"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"コピーしたテキストを閉じる"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"コピーしたテキストを編集"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"コピーした画像を編集"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"付近のデバイスに送信"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 79dde78..f3eb611 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"მზადაა"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"კოპირებულია"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g>-დან"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"კოპირებული ტექსტის უარყოფა"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"კოპირებული ტექსტის რედაქტირება"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"კოპირებული სურათის რედაქტირება"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ახლომახლო მოწყობილობაზე გაგზავნა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index d7e70ac..8d9be16 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Дайын"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Көшірілді"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> қолданбасынан"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Көшірілген мәтінді жою"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Көшірілген мәтінді өңдеу"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Көшірілген суретті өңдеу"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Маңайдағы құрылғыға жіберу"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 40cc7cc..4ae45ba 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -949,8 +949,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Atlikta"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Nukopijuota"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Iš „<xliff:g id="APPNAME">%1$s</xliff:g>“"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Atsisakyti nukopijuoto teksto"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Redaguoti nukopijuotą tekstą"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Redaguoti nukopijuotą vaizdą"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Siųsti į įrenginį netoliese"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 886d2dd..7c60823 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Готово"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Копирано"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Од <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Отфрли го копираниот текст"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Изменете го копираниот текст"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Изменете ја копираната слика"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Испратете до уред во близина"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 6298dff..26850c8 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"പൂർത്തിയായി"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"പകർത്തി"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> എന്നതിൽ നിന്ന്"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"പകർത്തിയ ടെക്സ്റ്റ് ഡിസ്മിസ് ചെയ്യുക"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"പകർത്തിയ ടെക്സ്റ്റ് എഡിറ്റ് ചെയ്യുക"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"പകർത്തിയ ചിത്രം എഡിറ്റ് ചെയ്യുക"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"സമീപത്തുള്ള ഉപകരണത്തിലേക്ക് അയയ്ക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index e9d0a03..55fae24 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Болсон"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Хууллаа"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g>-с"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Хуулсан текстийг үл хэрэгсэх"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Хуулсан текстийг засах"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Хуулсан зургийг засах"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Ойролцоох төхөөрөмж рүү илгээх"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 4e4d184..b0f3c8a 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -307,7 +307,7 @@
<string name="zen_alarms_introduction" msgid="3987266042682300470">"अलार्म व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string>
<string name="zen_priority_customize_button" msgid="4119213187257195047">"सानुकूलित करा"</string>
<string name="zen_silence_introduction_voice" msgid="853573681302712348">"हे अलार्म, संगीत, व्हिडिओ आणि गेमसह, सर्व आवाज आणि कंपने ब्लॉक करते. तरीही तुम्ही फोन कॉल करू शकतात."</string>
- <string name="zen_silence_introduction" msgid="6117517737057344014">"हे अलार्म, संगीत, व्हिडिओ आणि गेम यासह, सर्व आवाज आणि कंपने अवरोधित करते."</string>
+ <string name="zen_silence_introduction" msgid="6117517737057344014">"हे अलार्म, संगीत, व्हिडिओ आणि गेम यांसह, सर्व आवाज आणि व्हायब्रेशन ब्लॉक करते."</string>
<string name="notification_tap_again" msgid="4477318164947497249">"उघडण्यासाठी पुन्हा टॅप करा"</string>
<string name="tap_again" msgid="1315420114387908655">"पुन्हा टॅप करा"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"उघडण्यासाठी वर स्वाइप करा"</string>
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"पूर्ण झाले"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"कॉपी केले"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> वरून"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"कॉपी केलेला मजकूर डिसमिस करा"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"कॉपी केलेला मजकूर संपादित करा"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"कॉपी केलेली इमेज संपादित करा"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"जवळपासच्या डिव्हाइसवर पाठवा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 9096ac9..f8f6509 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Selesai"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Disalin"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Daripada <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Ketepikan teks yang disalin"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Edit teks yang disalin"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edit imej yang disalin"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Hantar ke peranti berdekatan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index f227516..3a341d5 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"ပြီးပြီ"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"ကူးပြီးပါပြီ"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> ထံမှ"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"မိတ္တူကူးထားသောစာသားကို ပယ်ရန်"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"ကူးထားသည့်စာသားကို တည်းဖြတ်ရန်"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"ကူးထားသည့်ပုံကို ပြင်ဆင်ရန်"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"အနီးတစ်ဝိုက်ရှိ စက်များသို့ ပို့ရန်"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 08cfddb..5fe026c 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"सम्पन्न भयो"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"कपी गरियो"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> बाट"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"कपी गरिएको टेक्स्ट हटाउनुहोस्"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"कपी गरिएको टेक्स्ट सम्पादन गर्नुहोस्"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"कपी गरिएको फोटो सम्पादन गर्नुहोस्"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"नजिकैको डिभाइसमा पठाउनुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index d1972e5..891bdc7 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Klaar"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Gekopieerd"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Uit <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Gekopieerde tekst sluiten"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Gekopieerde tekst bewerken"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Gekopieerde afbeelding bewerken"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Naar apparaat in de buurt sturen"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index c3d6fdf..97de165 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"ਹੋ ਗਿਆ"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"ਕਾਪੀ ਕੀਤੀ ਗਈ"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> ਤੋਂ"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"ਕਾਪੀ ਕੀਤੀ ਲਿਖਤ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"ਕਾਪੀ ਕੀਤੀ ਲਿਖਤ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"ਕਾਪੀ ਕੀਤੇ ਗਏ ਚਿੱਤਰ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸ \'ਤੇ ਭੇਜੋ"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index a62192f..d9753e9 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiado"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Do app <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Dispensar texto copiado"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar texto copiado"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar imagem copiada"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar para dispositivo próximo"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 790cbcc..c20554a 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluir"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiado"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Da app <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Ignorar texto copiado"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar texto copiado"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar imagem copiada"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar para dispositivo próximo"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a62192f..d9753e9 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiado"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Do app <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Dispensar texto copiado"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar texto copiado"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar imagem copiada"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar para dispositivo próximo"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 9b3d1e9..1d75ea6 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -942,8 +942,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Gata"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"S-a copiat"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Din <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Închideți textul copiat"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Editați textul copiat"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editați imaginea copiată"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Trimiteți către un dispozitiv din apropiere"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 963f5d2..d7ab354 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -922,7 +922,7 @@
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нет других доступных сетей"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"Нет доступных сетей"</string>
<string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
- <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Выберите сеть, чтобы подключиться"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Чтобы подключиться к сети, нажмите на ее название"</string>
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблокируйте, чтобы посмотреть сети Wi-Fi."</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Поиск сетей…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не удалось подключиться к сети"</string>
@@ -949,8 +949,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Готово"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Скопировано."</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Из приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Удалить скопированный текст"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Изменить скопированный текст"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Изменить скопированное изображение"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Отправить на устройство поблизости"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index d4ad16c..30b6fb1 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"නිමයි"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"පිටපත් කරන ලදි"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> සිට"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"පිටපත් කළ පෙළ අස් කරන්න"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"පිටපත් කළ පෙළ සංස්කරණය කරන්න"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"පිටපත් කළ රූපය සංස්කරණය කරන්න"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"අවට උපාංගය වෙත යවන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 67a4639..1d47520 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -949,8 +949,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Hotovo"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Skopírované"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Z aplikácie <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Zrušiť skopírovaný text"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Upraviť skopírovaný text"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Upraviť skopírovaný obrázok"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Odoslať do zariadenia v okolí"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 744e176..f831f4c 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Klar"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopierades"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Från <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Ta bort kopierad text"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Redigera kopierad text"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Redigera kopierad bild"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Skicka till enhet i närheten"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index fe7e309..3af6a6c 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Imemaliza"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Imenakiliwa"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Kutoka <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Ondoa maandishi yaliyonakiliwa"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Badilisha maandishi yaliyonakiliwa"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Badilisha picha iliyonakiliwa"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Tuma kwenye kifaa kilicho karibu"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 4cc7496..12ad0f7 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -910,7 +910,7 @@
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"வேறு நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
<string name="turn_on_wifi" msgid="1308379840799281023">"வைஃபை"</string>
- <string name="tap_a_network_to_connect" msgid="1565073330852369558">"இணையத்துடன் இணைய நெட்வொர்க்கைத் தட்டுங்கள்"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"இணைய நெட்வொர்க்கைத் தட்டுங்கள்"</string>
<string name="unlock_to_view_networks" msgid="5072880496312015676">"நெட்வொர்க்குகளைப் பார்க்க அன்லாக் செய்யுங்கள்"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"நெட்வொர்க்குகளைத் தேடுகிறது…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string>
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"முடிந்தது"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"நகலெடுக்கப்பட்டது"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸிலிருந்து"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"நகலெடுக்கப்பட்ட வார்த்தைகளை மூடுக"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"நகலெடுத்த வார்த்தைகளைத் திருத்து"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"நகலெடுத்த படத்தைத் திருத்து"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"அருகிலுள்ள சாதனத்திற்கு அனுப்பு"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 19f40e1..f0e953e 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"పూర్తయింది"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"కాపీ అయింది"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> నుండి"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"కాపీ చేసిన టెక్స్ట్ను విస్మరించండి"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"కాపీ చేసిన టెక్స్ట్ను ఎడిట్ చేయండి"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"కాపీ చేసిన ఇమేజ్లను ఎడిట్ చేయండి"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"సమీపంలోని పరికరానికి పంపండి"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 5db5ebf..db0915de 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Tapos na"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Nakopya"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Mula sa <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"I-dismiss ang nakopyang text"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"I-edit ang kinopyang text"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"I-edit ang kinopyang larawan"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Ipadala sa kalapit na device"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 9f7e1bc..51ef512 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Bitti"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopyalandı"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> uygulamasından"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Kopyalanan metni kapat"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Kopyalanan metni düzenle"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Kopyalanan resmi düzenle"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Yakındaki cihaza gönder"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index c67efa7..78f5344 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"ہو گیا"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"کاپی کر دیا گیا ہے"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> سے"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"کاپی کردہ ٹیکسٹ برخاست کریں"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"کاپی کردہ ٹیکسٹ میں ترمیم کریں"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"کاپی کردہ تصویر میں ترمیم کریں"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"قریبی آلے کو بھیجیں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 92b434b..de072d6 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Tayyor"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Nusxa olindi"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Manba: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Nusxalangan matnni yopish"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Nusxa olingan matnni tahrirlash"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Nusxa olingan rasmni tahrirlash"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Yaqin-atrofdagi qurilmaga yuborish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index a8e8b7d0..361b7b2 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Xong"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Đã sao chép"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Từ <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Loại bỏ văn bản đã sao chép"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Chỉnh sửa văn bản đã sao chép"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Chỉnh sửa hình ảnh đã sao chép"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Gửi đến thiết bị ở gần"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9e4894e..1ed22f6 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"完成"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"已复制"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"来自<xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"忽略复制的文字"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"修改所复制的文字"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"编辑所复制的图片"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"发送到附近的设备"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 4c7b9ce..2908ac7 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -935,8 +935,7 @@
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Kwenziwe"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Ikopishiwe"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Kusukela ku-<xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <!-- no translation found for clipboard_dismiss_description (3335990369850165486) -->
- <skip />
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Chitha umbhalo okopishiwe"</string>
<string name="clipboard_edit_text_description" msgid="805254383912962103">"Hlela umbhalo okopishiwe"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"Hlela umfanekiso okopishiwe"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Thumela kudivayisi eseduze"</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0fe744d..1dd41a3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2046,6 +2046,9 @@
<!-- Text for microphone app op [CHAR LIMIT=20]-->
<string name="privacy_type_microphone">microphone</string>
+ <!-- Text for media projection privacy type [CHAR LIMIT=20]-->
+ <string name="privacy_type_media_projection">screen recording</string>
+
<!-- What to show on the ambient display player when song doesn't have a title. [CHAR LIMIT=20] -->
<string name="music_controls_no_title">No title</string>
@@ -2281,6 +2284,8 @@
<string name="media_output_dialog_button_stop_casting">Stop casting</string>
<!-- Accessibility text describing purpose of media output dialog. [CHAR LIMIT=NONE] -->
<string name="media_output_dialog_accessibility_title">Available devices for audio output.</string>
+ <!-- Accessibility text describing purpose of seekbar in media output dialog. [CHAR LIMIT=NONE] -->
+ <string name="media_output_dialog_accessibility_seekbar">Volume</string>
<!-- Media Output Broadcast Dialog -->
<!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] -->
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 5b6ddd8..aeda20f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -54,6 +54,7 @@
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.MediaRouter2Manager;
+import android.media.projection.MediaProjectionManager;
import android.media.session.MediaSessionManager;
import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
@@ -321,6 +322,11 @@
}
@Provides
+ static MediaProjectionManager provideMediaProjectionManager(Context context) {
+ return context.getSystemService(MediaProjectionManager.class);
+ }
+
+ @Provides
static MediaRouter2Manager provideMediaRouter2Manager(Context context) {
return MediaRouter2Manager.getInstance(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 535eff8..366ef26 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -46,6 +46,7 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarComponent;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
+import com.android.systemui.privacy.PrivacyModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.settings.dagger.SettingsModule;
@@ -122,6 +123,7 @@
LogModule.class,
PeopleHubModule.class,
PluginModule.class,
+ PrivacyModule.class,
QsFrameTranslateModule.class,
ScreenshotModule.class,
SensorModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e0f9d3c..e379d76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2816,12 +2816,9 @@
}
}
- /**
- * Receive a wake event from outside this class (most likely bio auth).
- */
- public void onWake(boolean withUnlock) {
- Trace.beginSection("KeyguardViewMediator#onWake");
- mWakeAndUnlocking = withUnlock;
+ public void onWakeAndUnlocking() {
+ Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
+ mWakeAndUnlocking = true;
keyguardDone();
Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java b/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java
index 71bc7c2..ed3e109 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.media;
+import android.annotation.NonNull;
import android.content.Context;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -39,7 +40,7 @@
*
* @param token The token for the session. This value must never be null.
*/
- public MediaController create(MediaSession.Token token) {
+ public MediaController create(@NonNull MediaSession.Token token) {
return new MediaController(mContext, token);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 8bf2c6e..6a69d42 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -509,6 +509,11 @@
*/
private fun updateState(key: String, state: PlaybackState) {
mediaEntries.get(key)?.let {
+ val token = it.token
+ if (token == null) {
+ if (DEBUG) Log.d(TAG, "State updated, but token was null")
+ return
+ }
val actions = createActionsFromState(it.packageName,
mediaControllerFactory.create(it.token), UserHandle(it.userId))
val data = it.copy(
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index d4c4f216..93a29ef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -161,8 +161,9 @@
destroyed = false
mediaController?.unregisterCallback(this)
field = value
- mediaController = if (field.token != null) {
- mediaControllerFactory.create(field.token)
+ val token = field.token
+ mediaController = if (token != null) {
+ mediaControllerFactory.create(token)
} else {
null
}
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 ec472c6..5430ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -28,7 +28,6 @@
import androidx.annotation.NonNull;
import androidx.core.widget.CompoundButtonCompat;
-import com.android.settingslib.Utils;
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
@@ -257,9 +256,8 @@
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
false /* bFocused */);
final Drawable d = mContext.getDrawable(R.drawable.ic_add);
- d.setColorFilter(new PorterDuffColorFilter(
- Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
mTitleIcon.setImageDrawable(d);
+ mTitleIcon.setColorFilter(mController.getColorItemContent());
mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 731e177..05e8c02 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -307,6 +307,7 @@
.setContentText(contentText)
.setContentTitle(title)
.setOnlyAlertOnce(true)
+ .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
.setStyle(new Notification.BigTextStyle().bigText(contentText))
.setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasBatterySettings()) {
@@ -333,7 +334,6 @@
final Notification n = nb.build();
mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
- logEvent(BatteryWarningEvents.LowBatteryWarningEvent.LOW_BATTERY_NOTIFICATION);
}
private boolean showSevereLowBatteryDialog() {
@@ -615,6 +615,7 @@
Slog.i(TAG,
"show low battery warning: level=" + mBatteryLevel
+ " [" + mBucket + "] playSound=" + playSound);
+ logEvent(BatteryWarningEvents.LowBatteryWarningEvent.LOW_BATTERY_NOTIFICATION);
mPlaySound = playSound;
mWarning = true;
updateNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt b/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt
new file mode 100644
index 0000000..de34cd6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import com.android.internal.annotations.GuardedBy
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.withIncreasedIndent
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Monitors privacy items backed by app ops:
+ * - Mic & Camera
+ * - Location
+ *
+ * If [PrivacyConfig.micCameraAvailable] / [PrivacyConfig.locationAvailable] are disabled,
+ * the corresponding PrivacyItems will not be reported.
+ */
+@SysUISingleton
+class AppOpsPrivacyItemMonitor @Inject constructor(
+ private val appOpsController: AppOpsController,
+ private val userTracker: UserTracker,
+ private val privacyConfig: PrivacyConfig,
+ @Background private val bgExecutor: DelayableExecutor,
+ private val logger: PrivacyLogger
+) : PrivacyItemMonitor {
+
+ @VisibleForTesting
+ companion object {
+ val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_RECORD_AUDIO,
+ AppOpsManager.OP_PHONE_CALL_MICROPHONE,
+ AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO)
+ val OPS_LOCATION = intArrayOf(
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_FINE_LOCATION)
+ val OPS = OPS_MIC_CAMERA + OPS_LOCATION
+ val USER_INDEPENDENT_OPS = intArrayOf(AppOpsManager.OP_PHONE_CALL_CAMERA,
+ AppOpsManager.OP_PHONE_CALL_MICROPHONE)
+ }
+
+ private val lock = Any()
+
+ @GuardedBy("lock")
+ private var callback: PrivacyItemMonitor.Callback? = null
+ @GuardedBy("lock")
+ private var micCameraAvailable = privacyConfig.micCameraAvailable
+ @GuardedBy("lock")
+ private var locationAvailable = privacyConfig.locationAvailable
+ @GuardedBy("lock")
+ private var listening = false
+
+ private val appOpsCallback = object : AppOpsController.Callback {
+ override fun onActiveStateChanged(
+ code: Int,
+ uid: Int,
+ packageName: String,
+ active: Boolean
+ ) {
+ synchronized(lock) {
+ // Check if we care about this code right now
+ if (code in OPS_MIC_CAMERA && !micCameraAvailable) {
+ return
+ }
+ if (code in OPS_LOCATION && !locationAvailable) {
+ return
+ }
+ if (userTracker.userProfiles.any { it.id == UserHandle.getUserId(uid) } ||
+ code in USER_INDEPENDENT_OPS) {
+ logger.logUpdatedItemFromAppOps(code, uid, packageName, active)
+ dispatchOnPrivacyItemsChanged()
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ internal val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ onCurrentProfilesChanged()
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ onCurrentProfilesChanged()
+ }
+ }
+
+ private val configCallback = object : PrivacyConfig.Callback {
+ override fun onFlagLocationChanged(flag: Boolean) {
+ onFlagChanged()
+ }
+
+ override fun onFlagMicCameraChanged(flag: Boolean) {
+ onFlagChanged()
+ }
+
+ private fun onFlagChanged() {
+ synchronized(lock) {
+ micCameraAvailable = privacyConfig.micCameraAvailable
+ locationAvailable = privacyConfig.locationAvailable
+ setListeningStateLocked()
+ }
+ dispatchOnPrivacyItemsChanged()
+ }
+ }
+
+ init {
+ privacyConfig.addCallback(configCallback)
+ }
+
+ override fun startListening(callback: PrivacyItemMonitor.Callback) {
+ synchronized(lock) {
+ this.callback = callback
+ setListeningStateLocked()
+ }
+ }
+
+ override fun stopListening() {
+ synchronized(lock) {
+ this.callback = null
+ setListeningStateLocked()
+ }
+ }
+
+ /**
+ * Updates listening status based on whether there are callbacks and the indicators are enabled.
+ *
+ * Always listen to all OPS so we don't have to figure out what we should be listening to. We
+ * still have to filter anyway. Updates are filtered in the callback.
+ *
+ * This is only called from private (add/remove)Callback and from the config listener, all in
+ * main thread.
+ */
+ @GuardedBy("lock")
+ private fun setListeningStateLocked() {
+ val shouldListen = callback != null && (micCameraAvailable || locationAvailable)
+ if (listening == shouldListen) {
+ return
+ }
+
+ listening = shouldListen
+ if (shouldListen) {
+ appOpsController.addCallback(OPS, appOpsCallback)
+ userTracker.addCallback(userTrackerCallback, bgExecutor)
+ onCurrentProfilesChanged()
+ } else {
+ appOpsController.removeCallback(OPS, appOpsCallback)
+ userTracker.removeCallback(userTrackerCallback)
+ }
+ }
+
+ override fun getActivePrivacyItems(): List<PrivacyItem> {
+ val activeAppOps = appOpsController.getActiveAppOps(true)
+ val currentUserProfiles = userTracker.userProfiles
+
+ return synchronized(lock) {
+ activeAppOps.filter {
+ currentUserProfiles.any { user -> user.id == UserHandle.getUserId(it.uid) } ||
+ it.code in USER_INDEPENDENT_OPS
+ }.mapNotNull { toPrivacyItemLocked(it) }
+ }.distinct()
+ }
+
+ @GuardedBy("lock")
+ private fun privacyItemForAppOpEnabledLocked(code: Int): Boolean {
+ if (code in OPS_LOCATION) {
+ return locationAvailable
+ } else if (code in OPS_MIC_CAMERA) {
+ return micCameraAvailable
+ } else {
+ return false
+ }
+ }
+
+ @GuardedBy("lock")
+ private fun toPrivacyItemLocked(appOpItem: AppOpItem): PrivacyItem? {
+ if (!privacyItemForAppOpEnabledLocked(appOpItem.code)) {
+ return null
+ }
+ val type: PrivacyType = when (appOpItem.code) {
+ AppOpsManager.OP_PHONE_CALL_CAMERA,
+ AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_PHONE_CALL_MICROPHONE,
+ AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
+ AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
+ else -> return null
+ }
+ val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
+ return PrivacyItem(type, app, appOpItem.timeStartedElapsed, appOpItem.isDisabled)
+ }
+
+ private fun onCurrentProfilesChanged() {
+ val currentUserIds = userTracker.userProfiles.map { it.id }
+ logger.logCurrentProfilesChanged(currentUserIds)
+ dispatchOnPrivacyItemsChanged()
+ }
+
+ private fun dispatchOnPrivacyItemsChanged() {
+ val cb = synchronized(lock) { callback }
+ if (cb != null) {
+ bgExecutor.execute {
+ cb.onPrivacyItemsChanged()
+ }
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ val ipw = pw.asIndenting()
+ ipw.println("AppOpsPrivacyItemMonitor:")
+ ipw.withIncreasedIndent {
+ synchronized(lock) {
+ ipw.println("Listening: $listening")
+ ipw.println("micCameraAvailable: $micCameraAvailable")
+ ipw.println("locationAvailable: $locationAvailable")
+ ipw.println("Callback: $callback")
+ }
+ ipw.println("Current user ids: ${userTracker.userProfiles.map { it.id }}")
+ }
+ ipw.flush()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/MediaProjectionPrivacyItemMonitor.kt b/packages/SystemUI/src/com/android/systemui/privacy/MediaProjectionPrivacyItemMonitor.kt
new file mode 100644
index 0000000..9b5a675
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/MediaProjectionPrivacyItemMonitor.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.pm.PackageManager
+import android.media.projection.MediaProjectionInfo
+import android.media.projection.MediaProjectionManager
+import android.os.Handler
+import android.util.Log
+import androidx.annotation.WorkerThread
+import com.android.internal.annotations.GuardedBy
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.withIncreasedIndent
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Monitors the active media projection to update privacy items.
+ */
+@SysUISingleton
+class MediaProjectionPrivacyItemMonitor @Inject constructor(
+ private val mediaProjectionManager: MediaProjectionManager,
+ private val packageManager: PackageManager,
+ private val privacyConfig: PrivacyConfig,
+ @Background private val bgHandler: Handler,
+ private val systemClock: SystemClock,
+ private val logger: PrivacyLogger
+) : PrivacyItemMonitor {
+
+ companion object {
+ const val TAG = "MediaProjectionPrivacyItemMonitor"
+ const val DEBUG = false
+ }
+
+ private val lock = Any()
+
+ @GuardedBy("lock")
+ private var callback: PrivacyItemMonitor.Callback? = null
+
+ @GuardedBy("lock")
+ private var mediaProjectionAvailable = privacyConfig.mediaProjectionAvailable
+ @GuardedBy("lock")
+ private var listening = false
+
+ @GuardedBy("lock")
+ private val privacyItems = mutableListOf<PrivacyItem>()
+
+ private val optionsCallback = object : PrivacyConfig.Callback {
+ override fun onFlagMediaProjectionChanged(flag: Boolean) {
+ synchronized(lock) {
+ mediaProjectionAvailable = privacyConfig.mediaProjectionAvailable
+ setListeningStateLocked()
+ }
+ dispatchOnPrivacyItemsChanged()
+ }
+ }
+
+ private val mediaProjectionCallback = object : MediaProjectionManager.Callback() {
+ @WorkerThread
+ override fun onStart(info: MediaProjectionInfo) {
+ synchronized(lock) { onMediaProjectionStartedLocked(info) }
+ dispatchOnPrivacyItemsChanged()
+ }
+
+ @WorkerThread
+ override fun onStop(info: MediaProjectionInfo) {
+ synchronized(lock) { onMediaProjectionStoppedLocked(info) }
+ dispatchOnPrivacyItemsChanged()
+ }
+ }
+
+ init {
+ privacyConfig.addCallback(optionsCallback)
+ setListeningStateLocked()
+ }
+
+ override fun startListening(callback: PrivacyItemMonitor.Callback) {
+ synchronized(lock) {
+ this.callback = callback
+ }
+ }
+
+ override fun stopListening() {
+ synchronized(lock) {
+ this.callback = null
+ }
+ }
+
+ @GuardedBy("lock")
+ @WorkerThread
+ private fun onMediaProjectionStartedLocked(info: MediaProjectionInfo) {
+ if (DEBUG) Log.d(TAG, "onMediaProjectionStartedLocked: info=$info")
+ val item = makePrivacyItem(info)
+ privacyItems.add(item)
+ logItemActive(item, true)
+ }
+
+ @GuardedBy("lock")
+ @WorkerThread
+ private fun onMediaProjectionStoppedLocked(info: MediaProjectionInfo) {
+ if (DEBUG) Log.d(TAG, "onMediaProjectionStoppedLocked: info=$info")
+ val item = makePrivacyItem(info)
+ privacyItems.removeAt(privacyItems.indexOfFirst { it.application == item.application })
+ logItemActive(item, false)
+ }
+
+ @WorkerThread
+ private fun makePrivacyItem(info: MediaProjectionInfo): PrivacyItem {
+ val userId = info.userHandle.identifier
+ val uid = packageManager.getPackageUidAsUser(info.packageName, userId)
+ val app = PrivacyApplication(info.packageName, uid)
+ val now = systemClock.elapsedRealtime()
+ return PrivacyItem(PrivacyType.TYPE_MEDIA_PROJECTION, app, now)
+ }
+
+ private fun logItemActive(item: PrivacyItem, active: Boolean) {
+ logger.logUpdatedItemFromMediaProjection(
+ item.application.uid, item.application.packageName, active)
+ }
+
+ /**
+ * Updates listening status based on whether there are callbacks and the indicator is enabled.
+ */
+ @GuardedBy("lock")
+ private fun setListeningStateLocked() {
+ val shouldListen = mediaProjectionAvailable
+ if (DEBUG) {
+ Log.d(TAG, "shouldListen=$shouldListen, " +
+ "mediaProjectionAvailable=$mediaProjectionAvailable")
+ }
+ if (listening == shouldListen) {
+ return
+ }
+
+ listening = shouldListen
+ if (shouldListen) {
+ if (DEBUG) Log.d(TAG, "Registering MediaProjectionManager callback")
+ mediaProjectionManager.addCallback(mediaProjectionCallback, bgHandler)
+
+ val activeProjection = mediaProjectionManager.activeProjectionInfo
+ if (activeProjection != null) {
+ onMediaProjectionStartedLocked(activeProjection)
+ dispatchOnPrivacyItemsChanged()
+ }
+ } else {
+ if (DEBUG) Log.d(TAG, "Unregistering MediaProjectionManager callback")
+ mediaProjectionManager.removeCallback(mediaProjectionCallback)
+ privacyItems.forEach { logItemActive(it, false) }
+ privacyItems.clear()
+ dispatchOnPrivacyItemsChanged()
+ }
+ }
+
+ override fun getActivePrivacyItems(): List<PrivacyItem> {
+ synchronized(lock) {
+ if (DEBUG) Log.d(TAG, "getActivePrivacyItems: privacyItems=$privacyItems")
+ return privacyItems.toList()
+ }
+ }
+
+ private fun dispatchOnPrivacyItemsChanged() {
+ if (DEBUG) Log.d(TAG, "dispatchOnPrivacyItemsChanged")
+ val cb = synchronized(lock) { callback }
+ if (cb != null) {
+ bgHandler.post {
+ cb.onPrivacyItemsChanged()
+ }
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ val ipw = pw.asIndenting()
+ ipw.println("MediaProjectionPrivacyItemMonitor:")
+ ipw.withIncreasedIndent {
+ synchronized(lock) {
+ ipw.println("Listening: $listening")
+ ipw.println("mediaProjectionAvailable: $mediaProjectionAvailable")
+ ipw.println("Callback: $callback")
+ ipw.println("Privacy Items: $privacyItems")
+ }
+ }
+ ipw.flush()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
new file mode 100644
index 0000000..d652889
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.provider.DeviceConfig
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+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.util.DeviceConfigProxy
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.withIncreasedIndent
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+
+@SysUISingleton
+class PrivacyConfig @Inject constructor(
+ @Main private val uiExecutor: DelayableExecutor,
+ private val deviceConfigProxy: DeviceConfigProxy,
+ dumpManager: DumpManager
+) : Dumpable {
+
+ @VisibleForTesting
+ internal companion object {
+ const val TAG = "PrivacyConfig"
+ private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+ private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
+ private const val MEDIA_PROJECTION =
+ SystemUiDeviceConfigFlags.PROPERTY_MEDIA_PROJECTION_INDICATORS_ENABLED
+ private const val DEFAULT_MIC_CAMERA = true
+ private const val DEFAULT_LOCATION = false
+ private const val DEFAULT_MEDIA_PROJECTION = true
+ }
+
+ private val callbacks = mutableListOf<WeakReference<Callback>>()
+
+ var micCameraAvailable = isMicCameraEnabled()
+ private set
+ var locationAvailable = isLocationEnabled()
+ private set
+ var mediaProjectionAvailable = isMediaProjectionEnabled()
+ private set
+
+ private val devicePropertiesChangedListener =
+ DeviceConfig.OnPropertiesChangedListener { properties ->
+ if (DeviceConfig.NAMESPACE_PRIVACY == properties.namespace) {
+ // Running on the ui executor so can iterate on callbacks
+ if (properties.keyset.contains(MIC_CAMERA)) {
+ micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
+ callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
+ }
+
+ if (properties.keyset.contains(LOCATION)) {
+ locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
+ callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
+ }
+
+ if (properties.keyset.contains(MEDIA_PROJECTION)) {
+ mediaProjectionAvailable =
+ properties.getBoolean(MEDIA_PROJECTION, DEFAULT_MEDIA_PROJECTION)
+ callbacks.forEach {
+ it.get()?.onFlagMediaProjectionChanged(mediaProjectionAvailable)
+ }
+ }
+ }
+ }
+
+ init {
+ dumpManager.registerDumpable(TAG, this)
+ deviceConfigProxy.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ uiExecutor,
+ devicePropertiesChangedListener)
+ }
+
+ private fun isMicCameraEnabled(): Boolean {
+ return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ MIC_CAMERA, DEFAULT_MIC_CAMERA)
+ }
+
+ private fun isLocationEnabled(): Boolean {
+ return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ LOCATION, DEFAULT_LOCATION)
+ }
+
+ private fun isMediaProjectionEnabled(): Boolean {
+ return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ MEDIA_PROJECTION, DEFAULT_MEDIA_PROJECTION)
+ }
+
+ fun addCallback(callback: Callback) {
+ addCallback(WeakReference(callback))
+ }
+
+ fun removeCallback(callback: Callback) {
+ removeCallback(WeakReference(callback))
+ }
+
+ private fun addCallback(callback: WeakReference<Callback>) {
+ uiExecutor.execute {
+ callbacks.add(callback)
+ }
+ }
+
+ private fun removeCallback(callback: WeakReference<Callback>) {
+ uiExecutor.execute {
+ // Removes also if the callback is null
+ callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ val ipw = pw.asIndenting()
+ ipw.println("PrivacyConfig state:")
+ ipw.withIncreasedIndent {
+ ipw.println("micCameraAvailable: $micCameraAvailable")
+ ipw.println("locationAvailable: $locationAvailable")
+ ipw.println("mediaProjectionAvailable: $mediaProjectionAvailable")
+ ipw.println("Callbacks:")
+ ipw.withIncreasedIndent {
+ callbacks.forEach { callback ->
+ callback.get()?.let { ipw.println(it) }
+ }
+ }
+ }
+ ipw.flush()
+ }
+
+ interface Callback {
+ @JvmDefault
+ fun onFlagMicCameraChanged(flag: Boolean) {}
+
+ @JvmDefault
+ fun onFlagLocationChanged(flag: Boolean) {}
+
+ @JvmDefault
+ fun onFlagMediaProjectionChanged(flag: Boolean) {}
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index d4e1642..03145a7 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -165,6 +165,7 @@
PrivacyType.TYPE_LOCATION -> R.drawable.privacy_item_circle_location
PrivacyType.TYPE_CAMERA -> R.drawable.privacy_item_circle_camera
PrivacyType.TYPE_MICROPHONE -> R.drawable.privacy_item_circle_microphone
+ PrivacyType.TYPE_MEDIA_PROJECTION -> R.drawable.privacy_item_circle_media_projection
}) as LayerDrawable
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 76199bf..8b41000 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -43,6 +43,12 @@
com.android.internal.R.drawable.perm_group_location,
android.Manifest.permission_group.LOCATION,
"location"
+ ),
+ TYPE_MEDIA_PROJECTION(
+ R.string.privacy_type_media_projection,
+ R.drawable.stat_sys_cast,
+ android.Manifest.permission_group.UNDEFINED,
+ "media projection"
);
fun getName(context: Context) = context.resources.getString(nameId)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index cd6eb99..a676150 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -16,27 +16,18 @@
package com.android.systemui.privacy
-import android.app.AppOpsManager
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.pm.UserInfo
-import android.os.UserHandle
-import android.provider.DeviceConfig
import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
import com.android.systemui.Dumpable
-import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.privacy.logging.PrivacyLogger
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.asIndenting
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.withIncreasedIndent
import java.io.PrintWriter
import java.lang.ref.WeakReference
import java.util.concurrent.Executor
@@ -44,11 +35,10 @@
@SysUISingleton
class PrivacyItemController @Inject constructor(
- private val appOpsController: AppOpsController,
@Main uiExecutor: DelayableExecutor,
@Background private val bgExecutor: DelayableExecutor,
- private val deviceConfigProxy: DeviceConfigProxy,
- private val userTracker: UserTracker,
+ private val privacyConfig: PrivacyConfig,
+ private val privacyItemMonitors: Set<@JvmSuppressWildcards PrivacyItemMonitor>,
private val logger: PrivacyLogger,
private val systemClock: SystemClock,
dumpManager: DumpManager
@@ -56,24 +46,7 @@
@VisibleForTesting
internal companion object {
- val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_RECORD_AUDIO,
- AppOpsManager.OP_PHONE_CALL_MICROPHONE,
- AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO)
- val OPS_LOCATION = intArrayOf(
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION)
- val OPS = OPS_MIC_CAMERA + OPS_LOCATION
- val intentFilter = IntentFilter().apply {
- addAction(Intent.ACTION_USER_SWITCHED)
- addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
- addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
- }
const val TAG = "PrivacyItemController"
- private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
- private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
- private const val DEFAULT_MIC_CAMERA = true
- private const val DEFAULT_LOCATION = false
@VisibleForTesting const val TIME_TO_HOLD_INDICATORS = 5000L
}
@@ -82,23 +55,18 @@
@Synchronized get() = field.toList() // Returns a shallow copy of the list
@Synchronized set
- private fun isMicCameraEnabled(): Boolean {
- return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- MIC_CAMERA, DEFAULT_MIC_CAMERA)
- }
-
- private fun isLocationEnabled(): Boolean {
- return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- LOCATION, DEFAULT_LOCATION)
- }
-
- private var currentUserIds = emptyList<Int>()
private var listening = false
private val callbacks = mutableListOf<WeakReference<Callback>>()
private val internalUiExecutor = MyExecutor(uiExecutor)
-
private var holdingRunnableCanceler: Runnable? = null
+ val micCameraAvailable
+ get() = privacyConfig.micCameraAvailable
+ val locationAvailable
+ get() = privacyConfig.locationAvailable
+ val allIndicatorsAvailable
+ get() = micCameraAvailable && locationAvailable && privacyConfig.mediaProjectionAvailable
+
private val notifyChanges = Runnable {
val list = privacyList
callbacks.forEach { it.get()?.onPrivacyItemsChanged(list) }
@@ -109,90 +77,33 @@
uiExecutor.execute(notifyChanges)
}
- var micCameraAvailable = isMicCameraEnabled()
- private set
- var locationAvailable = isLocationEnabled()
+ private val optionsCallback = object : PrivacyConfig.Callback {
+ override fun onFlagLocationChanged(flag: Boolean) {
+ callbacks.forEach { it.get()?.onFlagLocationChanged(flag) }
+ }
- var allIndicatorsAvailable = micCameraAvailable && locationAvailable
+ override fun onFlagMicCameraChanged(flag: Boolean) {
+ callbacks.forEach { it.get()?.onFlagMicCameraChanged(flag) }
+ }
- private val devicePropertiesChangedListener =
- object : DeviceConfig.OnPropertiesChangedListener {
- override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
- if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
- (properties.keyset.contains(MIC_CAMERA) ||
- properties.keyset.contains(LOCATION))) {
-
- // Running on the ui executor so can iterate on callbacks
- if (properties.keyset.contains(MIC_CAMERA)) {
- micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
- allIndicatorsAvailable = micCameraAvailable && locationAvailable
- callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
- }
-
- if (properties.keyset.contains(LOCATION)) {
- locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
- allIndicatorsAvailable = micCameraAvailable && locationAvailable
- callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
- }
- internalUiExecutor.updateListeningState()
- }
+ override fun onFlagMediaProjectionChanged(flag: Boolean) {
+ callbacks.forEach { it.get()?.onFlagMediaProjectionChanged(flag) }
}
}
- private val cb = object : AppOpsController.Callback {
- override fun onActiveStateChanged(
- code: Int,
- uid: Int,
- packageName: String,
- active: Boolean
- ) {
- // Check if we care about this code right now
- if (code in OPS_LOCATION && !locationAvailable) {
- return
- }
- val userId = UserHandle.getUserId(uid)
- if (userId in currentUserIds ||
- code == AppOpsManager.OP_PHONE_CALL_MICROPHONE ||
- code == AppOpsManager.OP_PHONE_CALL_CAMERA) {
- logger.logUpdatedItemFromAppOps(code, uid, packageName, active)
- update(false)
- }
- }
- }
-
- @VisibleForTesting
- internal var userTrackerCallback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- update(true)
- }
-
- override fun onProfilesChanged(profiles: List<UserInfo>) {
- update(true)
+ private val privacyItemMonitorCallback = object : PrivacyItemMonitor.Callback {
+ override fun onPrivacyItemsChanged() {
+ update()
}
}
init {
- deviceConfigProxy.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_PRIVACY,
- uiExecutor,
- devicePropertiesChangedListener)
dumpManager.registerDumpable(TAG, this)
+ privacyConfig.addCallback(optionsCallback)
}
- private fun unregisterListener() {
- userTracker.removeCallback(userTrackerCallback)
- }
-
- private fun registerReceiver() {
- userTracker.addCallback(userTrackerCallback, bgExecutor)
- }
-
- private fun update(updateUsers: Boolean) {
+ private fun update() {
bgExecutor.execute {
- if (updateUsers) {
- currentUserIds = userTracker.userProfiles.map { it.id }
- logger.logCurrentProfilesChanged(currentUserIds)
- }
updateListAndNotifyChanges.run()
}
}
@@ -207,20 +118,17 @@
* main thread.
*/
private fun setListeningState() {
- val listen = !callbacks.isEmpty() and
- (micCameraAvailable || locationAvailable)
+ val listen = callbacks.isNotEmpty()
if (listening == listen) return
listening = listen
if (listening) {
- appOpsController.addCallback(OPS, cb)
- registerReceiver()
- update(true)
+ privacyItemMonitors.forEach { it.startListening(privacyItemMonitorCallback) }
+ update()
} else {
- appOpsController.removeCallback(OPS, cb)
- unregisterListener()
+ privacyItemMonitors.forEach { it.stopListening() }
// Make sure that we remove all indicators and notify listeners if we are not
// listening anymore due to indicators being disabled
- update(false)
+ update()
}
}
@@ -259,11 +167,7 @@
privacyList = emptyList()
return
}
- val list = appOpsController.getActiveAppOps(true).filter {
- UserHandle.getUserId(it.uid) in currentUserIds ||
- it.code == AppOpsManager.OP_PHONE_CALL_MICROPHONE ||
- it.code == AppOpsManager.OP_PHONE_CALL_CAMERA
- }.mapNotNull { toPrivacyItem(it) }.distinct()
+ val list = privacyItemMonitors.flatMap { it.getActivePrivacyItems() }.distinct()
privacyList = processNewList(list)
}
@@ -309,35 +213,11 @@
}
}
- private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
- val type: PrivacyType = when (appOpItem.code) {
- AppOpsManager.OP_PHONE_CALL_CAMERA,
- AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
- AppOpsManager.OP_PHONE_CALL_MICROPHONE,
- AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
- AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
- else -> return null
- }
- if (type == PrivacyType.TYPE_LOCATION && !locationAvailable) {
- return null
- }
- val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
- return PrivacyItem(type, app, appOpItem.timeStartedElapsed, appOpItem.isDisabled)
- }
-
- interface Callback {
+ interface Callback : PrivacyConfig.Callback {
fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
@JvmDefault
fun onFlagAllChanged(flag: Boolean) {}
-
- @JvmDefault
- fun onFlagMicCameraChanged(flag: Boolean) {}
-
- @JvmDefault
- fun onFlagLocationChanged(flag: Boolean) {}
}
private class NotifyChangesToCallback(
@@ -350,21 +230,34 @@
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("PrivacyItemController state:")
- pw.println(" Listening: $listening")
- pw.println(" Current user ids: $currentUserIds")
- pw.println(" Privacy Items:")
- privacyList.forEach {
- pw.print(" ")
- pw.println(it.toString())
- }
- pw.println(" Callbacks:")
- callbacks.forEach {
- it.get()?.let {
- pw.print(" ")
- pw.println(it.toString())
+ val ipw = pw.asIndenting()
+ ipw.println("PrivacyItemController state:")
+ ipw.withIncreasedIndent {
+ ipw.println("Listening: $listening")
+ ipw.println("Privacy Items:")
+ ipw.withIncreasedIndent {
+ privacyList.forEach {
+ ipw.println(it.toString())
+ }
+ }
+
+ ipw.println("Callbacks:")
+ ipw.withIncreasedIndent {
+ callbacks.forEach {
+ it.get()?.let {
+ ipw.println(it.toString())
+ }
+ }
+ }
+
+ ipw.println("PrivacyItemMonitors:")
+ ipw.withIncreasedIndent {
+ privacyItemMonitors.forEach {
+ it.dump(ipw, args)
+ }
}
}
+ ipw.flush()
}
private inner class MyExecutor(
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemMonitor.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemMonitor.kt
new file mode 100644
index 0000000..5bae31e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemMonitor.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.systemui.Dumpable
+
+interface PrivacyItemMonitor : Dumpable {
+ fun startListening(callback: Callback)
+ fun stopListening()
+ fun getActivePrivacyItems(): List<PrivacyItem>
+
+ interface Callback {
+ fun onPrivacyItemsChanged()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyModule.java b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyModule.java
new file mode 100644
index 0000000..732a310
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.IntoSet;
+
+/** Dagger module for privacy. */
+@Module
+public interface PrivacyModule {
+ /** Binds {@link AppOpsPrivacyItemMonitor} into the set of {@link PrivacyItemMonitor}. */
+ @Binds
+ @IntoSet
+ PrivacyItemMonitor bindAppOpsPrivacyItemMonitor(
+ AppOpsPrivacyItemMonitor appOpsPrivacyItemMonitor);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index 1a268b5..1ea9347 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -44,6 +44,16 @@
})
}
+ fun logUpdatedItemFromMediaProjection(uid: Int, packageName: String, active: Boolean) {
+ log(LogLevel.INFO, {
+ int1 = uid
+ str1 = packageName
+ bool1 = active
+ }, {
+ "MediaProjection: $str1($int1), active=$bool1"
+ })
+ }
+
fun logRetrievedPrivacyItemsList(list: List<PrivacyItem>) {
log(LogLevel.INFO, {
str1 = listToString(list)
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 32803fe..c1869e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -500,12 +500,10 @@
mUpdateMonitor.awakenFromDream();
}
mNotificationShadeWindowController.setNotificationShadeFocusable(false);
- mKeyguardViewMediator.onWake(true /* withUnlock */);
+ mKeyguardViewMediator.onWakeAndUnlocking();
Trace.endSection();
break;
case MODE_ONLY_WAKE:
- mKeyguardViewMediator.onWake(false /* withUnlock */);
- break;
case MODE_NONE:
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 4685c14..9a19d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -39,6 +39,8 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.dagger.PowerModule;
+import com.android.systemui.privacy.MediaProjectionPrivacyItemMonitor;
+import com.android.systemui.privacy.PrivacyItemMonitor;
import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
@@ -78,6 +80,7 @@
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.IntoSet;
/**
* A dagger module for injecting default implementations of components of System UI that may be
@@ -212,4 +215,12 @@
NotificationListener notificationListener) {
return new TvNotificationHandler(context, notificationListener);
}
+
+ /**
+ * Binds {@link MediaProjectionPrivacyItemMonitor} into the set of {@link PrivacyItemMonitor}.
+ */
+ @Binds
+ @IntoSet
+ abstract PrivacyItemMonitor bindMediaProjectionPrivacyItemMonitor(
+ MediaProjectionPrivacyItemMonitor mediaProjectionPrivacyItemMonitor);
}
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 9a7b129..e6df106 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -978,6 +978,26 @@
anyBoolean())
}
+ @Test
+ fun testPlaybackStateChange_keyHasNullToken_doesNothing() {
+ // When we get an update that sets the data's token to null
+ whenever(controller.metadata).thenReturn(metadataBuilder.build())
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.resumption).isFalse()
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(token = null))
+
+ // And then get a state update
+ val state = PlaybackState.Builder().build()
+ val callbackCaptor = argumentCaptor<(String, PlaybackState) -> Unit>()
+ verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
+
+ // Then no changes are made
+ callbackCaptor.value.invoke(KEY, state)
+ verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), anyInt(),
+ anyBoolean())
+ }
+
/**
* Helper function to add a media notification and capture the resulting MediaData
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index 890e4de..63dca3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -69,7 +69,7 @@
// Expressive applies hue rotations to the theme color. The input theme color has hue
// 117, ensuring the hue changed significantly is a strong signal styles are being applied.
ColorScheme colorScheme = new ColorScheme(wallpaperColors, false, Style.EXPRESSIVE);
- Assert.assertEquals(Cam.fromInt(colorScheme.getAccent1().get(6)).getHue(), 357.46, 0.1);
+ Assert.assertEquals(357.77, Cam.fromInt(colorScheme.getAccent1().get(6)).getHue(), 0.1);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
new file mode 100644
index 0000000..db96d55
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.AppOpsManager
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.hamcrest.Matchers.hasItem
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.nullValue
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThat
+import org.junit.Assert.assertTrue
+import org.junit.Assert.assertFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class AppOpsPrivacyItemMonitorTest : SysuiTestCase() {
+
+ companion object {
+ val CURRENT_USER_ID = 1
+ val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
+ const val TEST_PACKAGE_NAME = "test"
+
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+ fun <T> any(): T = Mockito.any<T>()
+ }
+
+ @Mock
+ private lateinit var appOpsController: AppOpsController
+
+ @Mock
+ private lateinit var callback: PrivacyItemMonitor.Callback
+
+ @Mock
+ private lateinit var userTracker: UserTracker
+
+ @Mock
+ private lateinit var privacyConfig: PrivacyConfig
+
+ @Mock
+ private lateinit var logger: PrivacyLogger
+
+ @Captor
+ private lateinit var argCaptorConfigCallback: ArgumentCaptor<PrivacyConfig.Callback>
+
+ @Captor
+ private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
+
+ private lateinit var appOpsPrivacyItemMonitor: AppOpsPrivacyItemMonitor
+ private lateinit var executor: FakeExecutor
+
+ fun createAppOpsPrivacyItemMonitor(): AppOpsPrivacyItemMonitor {
+ return AppOpsPrivacyItemMonitor(
+ appOpsController,
+ userTracker,
+ privacyConfig,
+ executor,
+ logger)
+ }
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ executor = FakeExecutor(FakeSystemClock())
+
+ // Listen to everything by default
+ `when`(privacyConfig.micCameraAvailable).thenReturn(true)
+ `when`(privacyConfig.locationAvailable).thenReturn(true)
+ `when`(userTracker.userProfiles).thenReturn(
+ listOf(UserInfo(CURRENT_USER_ID, TEST_PACKAGE_NAME, 0)))
+
+ appOpsPrivacyItemMonitor = createAppOpsPrivacyItemMonitor()
+ verify(privacyConfig).addCallback(capture(argCaptorConfigCallback))
+ }
+
+ @Test
+ fun testStartListeningAddsAppOpsCallback() {
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+ verify(appOpsController).addCallback(eq(AppOpsPrivacyItemMonitor.OPS), any())
+ }
+
+ @Test
+ fun testStopListeningRemovesAppOpsCallback() {
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+ verify(appOpsController, never()).removeCallback(any(), any())
+
+ appOpsPrivacyItemMonitor.stopListening()
+ executor.runAllReady()
+ verify(appOpsController).removeCallback(eq(AppOpsPrivacyItemMonitor.OPS), any())
+ }
+
+ @Test
+ fun testDistinctItems() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0)))
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
+
+ assertEquals(1, appOpsPrivacyItemMonitor.getActivePrivacyItems().size)
+ }
+
+ @Test
+ fun testSimilarItemsDifferentTimeStamp() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 1)))
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
+
+ assertEquals(2, appOpsPrivacyItemMonitor.getActivePrivacyItems().size)
+ }
+
+ @Test
+ fun testRegisterUserTrackerCallback() {
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+ verify(userTracker, atLeastOnce()).addCallback(
+ eq(appOpsPrivacyItemMonitor.userTrackerCallback), any())
+ verify(userTracker, never()).removeCallback(
+ eq(appOpsPrivacyItemMonitor.userTrackerCallback))
+ }
+
+ @Test
+ fun testUserTrackerCallback_userChanged() {
+ appOpsPrivacyItemMonitor.userTrackerCallback.onUserChanged(0, mContext)
+ executor.runAllReady()
+ verify(userTracker).userProfiles
+ }
+
+ @Test
+ fun testUserTrackerCallback_profilesChanged() {
+ appOpsPrivacyItemMonitor.userTrackerCallback.onProfilesChanged(emptyList())
+ executor.runAllReady()
+ verify(userTracker).userProfiles
+ }
+
+ @Test
+ fun testCallbackIsUpdated() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+ reset(callback)
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, TEST_PACKAGE_NAME, true)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged()
+ }
+
+ @Test
+ fun testRemoveCallback() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+ reset(callback)
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ appOpsPrivacyItemMonitor.stopListening()
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, TEST_PACKAGE_NAME, true)
+ executor.runAllReady()
+ verify(callback, never()).onPrivacyItemsChanged()
+ }
+
+ @Test
+ fun testListShouldNotHaveNull() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0)))
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
+
+ assertThat(appOpsPrivacyItemMonitor.getActivePrivacyItems(), not(hasItem(nullValue())))
+ }
+
+ @Test
+ fun testNotListeningWhenIndicatorsDisabled() {
+ changeMicCamera(false)
+ changeLocation(false)
+
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+ verify(appOpsController, never()).addCallback(eq(AppOpsPrivacyItemMonitor.OPS), any())
+ }
+
+ @Test
+ fun testNotSendingLocationWhenLocationDisabled() {
+ changeLocation(false)
+ executor.runAllReady()
+
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0)))
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
+
+ val privacyItems = appOpsPrivacyItemMonitor.getActivePrivacyItems()
+ assertEquals(1, privacyItems.size)
+ assertEquals(PrivacyType.TYPE_CAMERA, privacyItems[0].privacyType)
+ }
+
+ @Test
+ fun testNotUpdated_LocationChangeWhenLocationDisabled() {
+ doReturn(listOf(
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0)))
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
+
+ appOpsPrivacyItemMonitor.startListening(callback)
+ changeLocation(false)
+ executor.runAllReady()
+ reset(callback) // Clean callback
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+
+ verify(callback, never()).onPrivacyItemsChanged()
+ }
+
+ @Test
+ fun testLogActiveChanged() {
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+
+ verify(logger).logUpdatedItemFromAppOps(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+ }
+
+ @Test
+ fun testListRequestedShowPaused() {
+ appOpsPrivacyItemMonitor.getActivePrivacyItems()
+ verify(appOpsController).getActiveAppOps(true)
+ }
+
+ @Test
+ fun testListFilterCurrentUser() {
+ val otherUser = CURRENT_USER_ID + 1
+ val otherUserUid = otherUser * UserHandle.PER_USER_RANGE
+ `when`(userTracker.userProfiles)
+ .thenReturn(listOf(UserInfo(otherUser, TEST_PACKAGE_NAME, 0)))
+
+ doReturn(listOf(
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, otherUserUid, TEST_PACKAGE_NAME, 0))
+ ).`when`(appOpsController).getActiveAppOps(anyBoolean())
+
+ appOpsPrivacyItemMonitor.userTrackerCallback.onUserChanged(otherUser, mContext)
+ executor.runAllReady()
+
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+
+ val privacyItems = appOpsPrivacyItemMonitor.getActivePrivacyItems()
+
+ assertEquals(1, privacyItems.size)
+ assertEquals(PrivacyType.TYPE_CAMERA, privacyItems[0].privacyType)
+ assertEquals(otherUserUid, privacyItems[0].application.uid)
+ }
+
+ @Test
+ fun testAlwaysGetPhoneCameraOps() {
+ val otherUser = CURRENT_USER_ID + 1
+ `when`(userTracker.userProfiles)
+ .thenReturn(listOf(UserInfo(otherUser, TEST_PACKAGE_NAME, 0)))
+
+ doReturn(listOf(
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_PHONE_CALL_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0))
+ ).`when`(appOpsController).getActiveAppOps(anyBoolean())
+
+ appOpsPrivacyItemMonitor.userTrackerCallback.onUserChanged(otherUser, mContext)
+ executor.runAllReady()
+
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+
+ val privacyItems = appOpsPrivacyItemMonitor.getActivePrivacyItems()
+
+ assertEquals(1, privacyItems.size)
+ assertEquals(PrivacyType.TYPE_CAMERA, privacyItems[0].privacyType)
+ }
+
+ @Test
+ fun testAlwaysGetPhoneMicOps() {
+ val otherUser = CURRENT_USER_ID + 1
+ `when`(userTracker.userProfiles)
+ .thenReturn(listOf(UserInfo(otherUser, TEST_PACKAGE_NAME, 0)))
+
+ doReturn(listOf(
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),
+ AppOpItem(AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, 0))
+ ).`when`(appOpsController).getActiveAppOps(anyBoolean())
+
+ appOpsPrivacyItemMonitor.userTrackerCallback.onUserChanged(otherUser, mContext)
+ executor.runAllReady()
+
+ appOpsPrivacyItemMonitor.startListening(callback)
+ executor.runAllReady()
+
+ val privacyItems = appOpsPrivacyItemMonitor.getActivePrivacyItems()
+
+ assertEquals(1, privacyItems.size)
+ assertEquals(PrivacyType.TYPE_MICROPHONE, privacyItems[0].privacyType)
+ }
+
+ @Test
+ fun testDisabledAppOpIsPaused() {
+ val item = AppOpItem(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, 0)
+ item.isDisabled = true
+ `when`(appOpsController.getActiveAppOps(anyBoolean())).thenReturn(listOf(item))
+
+ val privacyItems = appOpsPrivacyItemMonitor.getActivePrivacyItems()
+ assertEquals(1, privacyItems.size)
+ assertTrue(privacyItems[0].paused)
+ }
+
+ @Test
+ fun testEnabledAppOpIsNotPaused() {
+ val item = AppOpItem(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, 0)
+ `when`(appOpsController.getActiveAppOps(anyBoolean())).thenReturn(listOf(item))
+
+ val privacyItems = appOpsPrivacyItemMonitor.getActivePrivacyItems()
+ assertEquals(1, privacyItems.size)
+ assertFalse(privacyItems[0].paused)
+ }
+
+ private fun changeMicCamera(value: Boolean) {
+ `when`(privacyConfig.micCameraAvailable).thenReturn(value)
+ argCaptorConfigCallback.value.onFlagMicCameraChanged(value)
+ }
+
+ private fun changeLocation(value: Boolean) {
+ `when`(privacyConfig.locationAvailable).thenReturn(value)
+ argCaptorConfigCallback.value.onFlagLocationChanged(value)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
new file mode 100644
index 0000000..272f149
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PrivacyConfigFlagsTest : SysuiTestCase() {
+ companion object {
+ private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+ private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
+ private const val MEDIA_PROJECTION =
+ SystemUiDeviceConfigFlags.PROPERTY_MEDIA_PROJECTION_INDICATORS_ENABLED
+ }
+
+ private lateinit var privacyConfig: PrivacyConfig
+
+ @Mock
+ private lateinit var callback: PrivacyConfig.Callback
+ @Mock
+ private lateinit var dumpManager: DumpManager
+
+ private lateinit var executor: FakeExecutor
+ private lateinit var deviceConfigProxy: DeviceConfigProxy
+
+ fun createPrivacyConfig(): PrivacyConfig {
+ return PrivacyConfig(
+ executor,
+ deviceConfigProxy,
+ dumpManager)
+ }
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ executor = FakeExecutor(FakeSystemClock())
+ deviceConfigProxy = DeviceConfigProxyFake()
+
+ privacyConfig = createPrivacyConfig()
+ privacyConfig.addCallback(callback)
+
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testMicCameraListeningByDefault() {
+ assertTrue(privacyConfig.micCameraAvailable)
+ }
+
+ @Test
+ fun testMicCameraChanged() {
+ changeMicCamera(false) // default is true
+ executor.runAllReady()
+
+ verify(callback).onFlagMicCameraChanged(false)
+
+ assertFalse(privacyConfig.micCameraAvailable)
+ }
+
+ @Test
+ fun testMediaProjectionChanged() {
+ changeMediaProjection(false) // default is true
+ executor.runAllReady()
+
+ verify(callback).onFlagMediaProjectionChanged(false)
+
+ assertFalse(privacyConfig.mediaProjectionAvailable)
+ }
+
+ @Test
+ fun testLocationChanged() {
+ changeLocation(true)
+ executor.runAllReady()
+
+ verify(callback).onFlagLocationChanged(true)
+ assertTrue(privacyConfig.locationAvailable)
+ }
+
+ @Test
+ fun testMicCamAndLocationChanged() {
+ changeLocation(true)
+ changeMicCamera(false)
+ executor.runAllReady()
+
+ verify(callback, atLeastOnce()).onFlagLocationChanged(true)
+ verify(callback, atLeastOnce()).onFlagMicCameraChanged(false)
+
+ assertTrue(privacyConfig.locationAvailable)
+ assertFalse(privacyConfig.micCameraAvailable)
+ }
+
+ @Test
+ fun testMicDeleted_stillAvailable() {
+ changeMicCamera(true)
+ executor.runAllReady()
+ changeMicCamera(null)
+ executor.runAllReady()
+
+ verify(callback, never()).onFlagMicCameraChanged(false)
+ assertTrue(privacyConfig.micCameraAvailable)
+ }
+
+ private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+ private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value)
+ private fun changeMediaProjection(value: Boolean?) = changeProperty(MEDIA_PROJECTION, value)
+
+ private fun changeProperty(name: String, value: Boolean?) {
+ deviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ name,
+ value?.toString(),
+ false
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
deleted file mode 100644
index 2a8611f..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ /dev/null
@@ -1,193 +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.systemui.privacy
-
-import android.provider.DeviceConfig
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.appops.AppOpsController
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.privacy.logging.PrivacyLogger
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.util.DeviceConfigProxy
-import com.android.systemui.util.DeviceConfigProxyFake
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class PrivacyItemControllerFlagsTest : SysuiTestCase() {
- companion object {
- fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
- fun <T> eq(value: T): T = Mockito.eq(value) ?: value
- fun <T> any(): T = Mockito.any<T>()
-
- private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
- private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
- }
-
- @Mock
- private lateinit var appOpsController: AppOpsController
- @Mock
- private lateinit var callback: PrivacyItemController.Callback
- @Mock
- private lateinit var dumpManager: DumpManager
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var logger: PrivacyLogger
-
- private lateinit var privacyItemController: PrivacyItemController
- private lateinit var executor: FakeExecutor
- private lateinit var deviceConfigProxy: DeviceConfigProxy
-
- fun createPrivacyItemController(): PrivacyItemController {
- return PrivacyItemController(
- appOpsController,
- executor,
- executor,
- deviceConfigProxy,
- userTracker,
- logger,
- FakeSystemClock(),
- dumpManager)
- }
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- executor = FakeExecutor(FakeSystemClock())
- deviceConfigProxy = DeviceConfigProxyFake()
-
- privacyItemController = createPrivacyItemController()
- privacyItemController.addCallback(callback)
-
- executor.runAllReady()
- }
-
- @Test
- fun testMicCameraListeningByDefault() {
- assertTrue(privacyItemController.micCameraAvailable)
- }
-
- @Test
- fun testMicCameraChanged() {
- changeMicCamera(false) // default is true
- executor.runAllReady()
-
- verify(callback).onFlagMicCameraChanged(false)
-
- assertFalse(privacyItemController.micCameraAvailable)
- }
-
- @Test
- fun testLocationChanged() {
- changeLocation(true)
- executor.runAllReady()
-
- verify(callback).onFlagLocationChanged(true)
- assertTrue(privacyItemController.locationAvailable)
- }
-
- @Test
- fun testBothChanged() {
- changeAll(true)
- changeMicCamera(false)
- executor.runAllReady()
-
- verify(callback, atLeastOnce()).onFlagLocationChanged(true)
- verify(callback, atLeastOnce()).onFlagMicCameraChanged(false)
-
- assertTrue(privacyItemController.locationAvailable)
- assertFalse(privacyItemController.micCameraAvailable)
- }
-
- @Test
- fun testAll_listeningToAll() {
- changeAll(true)
- executor.runAllReady()
-
- verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
- }
-
- @Test
- fun testMicCamera_listening() {
- changeMicCamera(true)
- executor.runAllReady()
-
- verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
- }
-
- @Test
- fun testLocation_listening() {
- changeLocation(true)
- executor.runAllReady()
-
- verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
- }
-
- @Test
- fun testAllFalse_notListening() {
- changeAll(true)
- executor.runAllReady()
- changeAll(false)
- changeMicCamera(false)
- executor.runAllReady()
-
- verify(appOpsController).removeCallback(any(), any())
- }
-
- @Test
- fun testMicDeleted_stillListening() {
- changeMicCamera(true)
- executor.runAllReady()
- changeMicCamera(null)
- executor.runAllReady()
-
- verify(appOpsController, never()).removeCallback(any(), any())
- }
-
- private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
- private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value)
- private fun changeAll(value: Boolean?) {
- changeMicCamera(value)
- changeLocation(value)
- }
-
- private fun changeProperty(name: String, value: Boolean?) {
- deviceConfigProxy.setProperty(
- DeviceConfig.NAMESPACE_PRIVACY,
- name,
- value?.toString(),
- false
- )
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index e4d7b1b..d563632 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -17,36 +17,24 @@
package com.android.systemui.privacy
import android.app.ActivityManager
-import android.app.AppOpsManager
-import android.content.pm.UserInfo
import android.os.UserHandle
-import android.provider.DeviceConfig
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.appops.AppOpItem
-import com.android.systemui.appops.AppOpsController
import com.android.systemui.dump.DumpManager
import com.android.systemui.privacy.logging.PrivacyLogger
-import com.android.systemui.settings.UserTracker
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.time.FakeSystemClock
-import org.hamcrest.Matchers.hasItem
-import org.hamcrest.Matchers.not
-import org.hamcrest.Matchers.nullValue
import org.junit.Assert.assertEquals
-import org.junit.Assert.assertThat
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyList
import org.mockito.Captor
import org.mockito.Mock
@@ -71,20 +59,18 @@
val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
const val TEST_PACKAGE_NAME = "test"
- private const val LOCATION_INDICATOR =
- SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
- private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
- fun <T> eq(value: T): T = Mockito.eq(value) ?: value
fun <T> any(): T = Mockito.any<T>()
}
@Mock
- private lateinit var appOpsController: AppOpsController
- @Mock
private lateinit var callback: PrivacyItemController.Callback
@Mock
- private lateinit var userTracker: UserTracker
+ private lateinit var privacyConfig: PrivacyConfig
+ @Mock
+ private lateinit var privacyItemMonitor: PrivacyItemMonitor
+ @Mock
+ private lateinit var privacyItemMonitor2: PrivacyItemMonitor
@Mock
private lateinit var dumpManager: DumpManager
@Mock
@@ -92,23 +78,21 @@
@Captor
private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
@Captor
- private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
+ private lateinit var argCaptorCallback: ArgumentCaptor<PrivacyItemMonitor.Callback>
+ @Captor
+ private lateinit var argCaptorConfigCallback: ArgumentCaptor<PrivacyConfig.Callback>
private lateinit var privacyItemController: PrivacyItemController
private lateinit var executor: FakeExecutor
private lateinit var fakeClock: FakeSystemClock
private lateinit var deviceConfigProxy: DeviceConfigProxy
- private val elapsedTime: Long
- get() = fakeClock.elapsedRealtime()
-
fun createPrivacyItemController(): PrivacyItemController {
return PrivacyItemController(
- appOpsController,
executor,
executor,
- deviceConfigProxy,
- userTracker,
+ privacyConfig,
+ setOf(privacyItemMonitor, privacyItemMonitor2),
logger,
fakeClock,
dumpManager)
@@ -120,43 +104,61 @@
fakeClock = FakeSystemClock()
executor = FakeExecutor(fakeClock)
deviceConfigProxy = DeviceConfigProxyFake()
-
- // Listen to everything by default
- changeMicCamera(true)
- changeLocation(true)
-
- `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(CURRENT_USER_ID, "", 0)))
-
privacyItemController = createPrivacyItemController()
}
@Test
- fun testSetListeningTrueByAddingCallback() {
+ fun testStartListeningByAddingCallback() {
privacyItemController.addCallback(callback)
executor.runAllReady()
- verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
- any())
+ verify(privacyItemMonitor).startListening(any())
+ verify(privacyItemMonitor2).startListening(any())
verify(callback).onPrivacyItemsChanged(anyList())
}
@Test
- fun testSetListeningFalseByRemovingLastCallback() {
+ fun testStopListeningByRemovingLastCallback() {
privacyItemController.addCallback(callback)
executor.runAllReady()
- verify(appOpsController, never()).removeCallback(any(),
- any())
+ verify(privacyItemMonitor, never()).stopListening()
privacyItemController.removeCallback(callback)
executor.runAllReady()
- verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
- any())
+ verify(privacyItemMonitor).stopListening()
+ verify(privacyItemMonitor2).stopListening()
verify(callback).onPrivacyItemsChanged(emptyList())
}
@Test
+ fun testPrivacyItemsAggregated() {
+ val item1 = PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0)
+ val item2 = PrivacyItem(PrivacyType.TYPE_MICROPHONE,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 1)
+ doReturn(listOf(item1))
+ .`when`(privacyItemMonitor).getActivePrivacyItems()
+ doReturn(listOf(item2))
+ .`when`(privacyItemMonitor2).getActivePrivacyItems()
+
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+ assertEquals(2, argCaptor.value.size)
+ assertTrue(argCaptor.value.contains(item1))
+ assertTrue(argCaptor.value.contains(item2))
+ }
+
+ @Test
fun testDistinctItems() {
- doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
- AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOps(anyBoolean())
+ doReturn(listOf(
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0),
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0)))
+ .`when`(privacyItemMonitor).getActivePrivacyItems()
+ doReturn(listOf(
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0)))
+ .`when`(privacyItemMonitor2).getActivePrivacyItems()
privacyItemController.addCallback(callback)
executor.runAllReady()
@@ -166,9 +168,12 @@
@Test
fun testSimilarItemsDifferentTimeStamp() {
- doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
- AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
- .`when`(appOpsController).getActiveAppOps(anyBoolean())
+ doReturn(listOf(
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0),
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 1)))
+ .`when`(privacyItemMonitor).getActivePrivacyItems()
privacyItemController.addCallback(callback)
executor.runAllReady()
@@ -177,29 +182,6 @@
}
@Test
- fun testRegisterCallback() {
- privacyItemController.addCallback(callback)
- executor.runAllReady()
- verify(userTracker, atLeastOnce()).addCallback(
- eq(privacyItemController.userTrackerCallback), any())
- verify(userTracker, never()).removeCallback(eq(privacyItemController.userTrackerCallback))
- }
-
- @Test
- fun testCallback_userChanged() {
- privacyItemController.userTrackerCallback.onUserChanged(0, mContext)
- executor.runAllReady()
- verify(userTracker).userProfiles
- }
-
- @Test
- fun testReceiver_profilesChanged() {
- privacyItemController.userTrackerCallback.onProfilesChanged(emptyList())
- executor.runAllReady()
- verify(userTracker).userProfiles
- }
-
- @Test
fun testAddMultipleCallbacks() {
val otherCallback = mock(PrivacyItemController.Callback::class.java)
privacyItemController.addCallback(callback)
@@ -215,7 +197,7 @@
@Test
fun testMultipleCallbacksAreUpdated() {
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems()
val otherCallback = mock(PrivacyItemController.Callback::class.java)
privacyItemController.addCallback(callback)
@@ -224,8 +206,8 @@
reset(callback)
reset(otherCallback)
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ verify(privacyItemMonitor).startListening(capture(argCaptorCallback))
+ argCaptorCallback.value.onPrivacyItemsChanged()
executor.runAllReady()
verify(callback).onPrivacyItemsChanged(anyList())
verify(otherCallback).onPrivacyItemsChanged(anyList())
@@ -233,7 +215,7 @@
@Test
fun testRemoveCallback() {
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems()
val otherCallback = mock(PrivacyItemController.Callback::class.java)
privacyItemController.addCallback(callback)
privacyItemController.addCallback(otherCallback)
@@ -242,32 +224,18 @@
reset(callback)
reset(otherCallback)
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ verify(privacyItemMonitor).startListening(capture(argCaptorCallback))
privacyItemController.removeCallback(callback)
- argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ argCaptorCallback.value.onPrivacyItemsChanged()
executor.runAllReady()
verify(callback, never()).onPrivacyItemsChanged(anyList())
verify(otherCallback).onPrivacyItemsChanged(anyList())
}
@Test
- fun testListShouldNotHaveNull() {
- doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
- AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOps(anyBoolean())
- privacyItemController.addCallback(callback)
- executor.runAllReady()
- executor.runAllReady()
-
- verify(callback).onPrivacyItemsChanged(capture(argCaptor))
- assertEquals(1, argCaptor.value.size)
- assertThat(argCaptor.value, not(hasItem(nullValue())))
- }
-
- @Test
fun testListShouldBeCopy() {
val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
- PrivacyApplication("", TEST_UID), 0))
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0))
privacyItemController.privacyList = list
val privacyList = privacyItemController.privacyList
assertEquals(list, privacyList)
@@ -275,174 +243,35 @@
}
@Test
- fun testNotListeningWhenIndicatorsDisabled() {
- changeLocation(false)
- changeMicCamera(false)
- privacyItemController.addCallback(callback)
- executor.runAllReady()
- verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
- any())
- }
-
- @Test
- fun testNotSendingLocationWhenOnlyMicCamera() {
- changeLocation(false)
- changeMicCamera(true)
- executor.runAllReady()
-
- doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
- AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOps(anyBoolean())
-
- privacyItemController.addCallback(callback)
- executor.runAllReady()
-
- verify(callback).onPrivacyItemsChanged(capture(argCaptor))
-
- assertEquals(1, argCaptor.value.size)
- assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType)
- }
-
- @Test
- fun testNotUpdated_LocationChangeWhenOnlyMicCamera() {
- doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOps(anyBoolean())
-
- privacyItemController.addCallback(callback)
- changeLocation(false)
- changeMicCamera(true)
- executor.runAllReady()
- reset(callback) // Clean callback
-
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(
- AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
-
- verify(callback, never()).onPrivacyItemsChanged(any())
- }
-
- @Test
- fun testLogActiveChanged() {
- privacyItemController.addCallback(callback)
- executor.runAllReady()
-
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(
- AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
-
- verify(logger).logUpdatedItemFromAppOps(
- AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
- }
-
- @Test
fun testLogListUpdated() {
- doReturn(listOf(
- AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0))
- ).`when`(appOpsController).getActiveAppOps(anyBoolean())
-
- privacyItemController.addCallback(callback)
- executor.runAllReady()
-
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(
- AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
- executor.runAllReady()
-
- val expected = PrivacyItem(
+ val privacyItem = PrivacyItem(
PrivacyType.TYPE_LOCATION,
PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID),
0
)
+ doReturn(listOf(privacyItem)).`when`(privacyItemMonitor).getActivePrivacyItems()
+
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+
+ verify(privacyItemMonitor).startListening(capture(argCaptorCallback))
+ argCaptorCallback.value.onPrivacyItemsChanged()
+ executor.runAllReady()
+
val captor = argumentCaptor<List<PrivacyItem>>()
verify(logger, atLeastOnce()).logRetrievedPrivacyItemsList(capture(captor))
// Let's look at the last log
val values = captor.allValues
- assertTrue(values[values.size - 1].contains(expected))
- }
-
- @Test
- fun testListRequestedShowPaused() {
- privacyItemController.addCallback(callback)
- executor.runAllReady()
- verify(appOpsController).getActiveAppOps(true)
- }
-
- @Test
- fun testListFilterCurrentUser() {
- val otherUser = CURRENT_USER_ID + 1
- val otherUserUid = otherUser * UserHandle.PER_USER_RANGE
- `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(otherUser, "", 0)))
-
- doReturn(listOf(
- AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
- AppOpItem(AppOpsManager.OP_CAMERA, otherUserUid, TEST_PACKAGE_NAME, 0))
- ).`when`(appOpsController).getActiveAppOps(anyBoolean())
-
- privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
- executor.runAllReady()
-
- privacyItemController.addCallback(callback)
- executor.runAllReady()
-
- verify(callback).onPrivacyItemsChanged(capture(argCaptor))
-
- assertEquals(1, argCaptor.value.size)
- assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType)
- assertEquals(otherUserUid, argCaptor.value[0].application.uid)
- }
-
- @Test
- fun testAlwaysGetPhoneCameraOps() {
- val otherUser = CURRENT_USER_ID + 1
- `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(otherUser, "", 0)))
-
- doReturn(listOf(
- AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
- AppOpItem(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, 0),
- AppOpItem(AppOpsManager.OP_PHONE_CALL_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0))
- ).`when`(appOpsController).getActiveAppOps(anyBoolean())
-
- privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
- executor.runAllReady()
-
- privacyItemController.addCallback(callback)
- executor.runAllReady()
-
- verify(callback).onPrivacyItemsChanged(capture(argCaptor))
-
- assertEquals(1, argCaptor.value.size)
- assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType)
- }
-
- @Test
- fun testAlwaysGetPhoneMicOps() {
- val otherUser = CURRENT_USER_ID + 1
- `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(otherUser, "", 0)))
-
- doReturn(listOf(
- AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
- AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),
- AppOpItem(AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, 0))
- ).`when`(appOpsController).getActiveAppOps(anyBoolean())
-
- privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
- executor.runAllReady()
-
- privacyItemController.addCallback(callback)
- executor.runAllReady()
-
- verify(callback).onPrivacyItemsChanged(capture(argCaptor))
-
- assertEquals(1, argCaptor.value.size)
- assertEquals(PrivacyType.TYPE_MICROPHONE, argCaptor.value[0].privacyType)
+ assertTrue(values[values.size - 1].contains(privacyItem))
}
@Test
fun testPassageOfTimeDoesNotRemoveIndicators() {
doReturn(listOf(
- AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
- )).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0)
+ )).`when`(privacyItemMonitor).getActivePrivacyItems()
privacyItemController.addCallback(callback)
@@ -457,18 +286,18 @@
fun testNotHeldAfterTimeIsOff() {
// Start with some element at time 0
doReturn(listOf(
- AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
- )).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0)
+ )).`when`(privacyItemMonitor).getActivePrivacyItems()
privacyItemController.addCallback(callback)
executor.runAllReady()
// Then remove it at time HOLD + 1
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems()
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS + 1)
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(
- AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
+ verify(privacyItemMonitor).startListening(capture(argCaptorCallback))
+ argCaptorCallback.value.onPrivacyItemsChanged()
executor.runAllReady()
// See it's not there
@@ -478,20 +307,21 @@
@Test
fun testElementNotRemovedBeforeHoldTime() {
- // Start with some element at time 0
+ // Start with some element at current time
doReturn(listOf(
- AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
- )).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID),
+ fakeClock.elapsedRealtime())
+ )).`when`(privacyItemMonitor).getActivePrivacyItems()
privacyItemController.addCallback(callback)
executor.runAllReady()
// Then remove it at time HOLD - 1
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems()
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1)
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(
- AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
+ verify(privacyItemMonitor).startListening(capture(argCaptorCallback))
+ argCaptorCallback.value.onPrivacyItemsChanged()
executor.runAllReady()
// See it's still there
@@ -503,18 +333,18 @@
fun testElementAutoRemovedAfterHoldTime() {
// Start with some element at time 0
doReturn(listOf(
- AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
- )).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0)
+ )).`when`(privacyItemMonitor).getActivePrivacyItems()
privacyItemController.addCallback(callback)
executor.runAllReady()
// Then remove it at time HOLD - 1
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
+ doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems()
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1)
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(
- AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
+ verify(privacyItemMonitor).startListening(capture(argCaptorCallback))
+ argCaptorCallback.value.onPrivacyItemsChanged()
executor.runAllReady()
fakeClock.advanceTime(2L)
@@ -526,38 +356,65 @@
}
@Test
+ fun testFlagsAll_listeningToAll() {
+ verify(privacyConfig).addCallback(capture(argCaptorConfigCallback))
+ privacyItemController.addCallback(callback)
+ `when`(privacyConfig.micCameraAvailable).thenReturn(true)
+ `when`(privacyConfig.locationAvailable).thenReturn(true)
+ `when`(privacyConfig.mediaProjectionAvailable).thenReturn(true)
+ argCaptorConfigCallback.value.onFlagMicCameraChanged(true)
+ argCaptorConfigCallback.value.onFlagLocationChanged(true)
+ argCaptorConfigCallback.value.onFlagMediaProjectionChanged(true)
+ executor.runAllReady()
+
+ assertTrue(privacyItemController.allIndicatorsAvailable)
+ }
+
+ @Test
+ fun testFlags_onFlagMicCameraChanged() {
+ verify(privacyConfig).addCallback(capture(argCaptorConfigCallback))
+ privacyItemController.addCallback(callback)
+ `when`(privacyConfig.micCameraAvailable).thenReturn(true)
+ argCaptorConfigCallback.value.onFlagMicCameraChanged(true)
+ executor.runAllReady()
+
+ assertTrue(privacyItemController.micCameraAvailable)
+ verify(callback).onFlagMicCameraChanged(true)
+ }
+
+ @Test
+ fun testFlags_onFlagLocationChanged() {
+ verify(privacyConfig).addCallback(capture(argCaptorConfigCallback))
+ privacyItemController.addCallback(callback)
+ `when`(privacyConfig.locationAvailable).thenReturn(true)
+ argCaptorConfigCallback.value.onFlagLocationChanged(true)
+ executor.runAllReady()
+
+ assertTrue(privacyItemController.locationAvailable)
+ verify(callback).onFlagLocationChanged(true)
+ }
+
+ @Test
+ fun testFlags_onFlagMediaProjectionChanged() {
+ verify(privacyConfig).addCallback(capture(argCaptorConfigCallback))
+ privacyItemController.addCallback(callback)
+ `when`(privacyConfig.mediaProjectionAvailable).thenReturn(true)
+ argCaptorConfigCallback.value.onFlagMediaProjectionChanged(true)
+ executor.runAllReady()
+
+ verify(callback).onFlagMediaProjectionChanged(true)
+ }
+
+ @Test
fun testPausedElementsAreRemoved() {
- val item = AppOpItem(
- AppOpsManager.OP_RECORD_AUDIO,
- TEST_UID,
- TEST_PACKAGE_NAME,
- elapsedTime
- )
- `when`(appOpsController.getActiveAppOps(anyBoolean())).thenReturn(listOf(item))
+ doReturn(listOf(
+ PrivacyItem(PrivacyType.TYPE_MICROPHONE,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0, true)))
+ .`when`(privacyItemMonitor).getActivePrivacyItems()
+
privacyItemController.addCallback(callback)
executor.runAllReady()
- item.isDisabled = true
- fakeClock.advanceTime(1)
- verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(
- AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
-
- executor.runAllReady()
-
- verify(callback).onPrivacyItemsChanged(emptyList())
assertTrue(privacyItemController.privacyList.isEmpty())
}
-
- private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
- private fun changeLocation(value: Boolean?) = changeProperty(LOCATION_INDICATOR, value)
-
- private fun changeProperty(name: String, value: Boolean?) {
- deviceConfigProxy.setProperty(
- DeviceConfig.NAMESPACE_PRIVACY,
- name,
- value?.toString(),
- false
- )
- }
}
\ No newline at end of file
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 5c60c70..d6a2f0f 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
@@ -180,29 +180,12 @@
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mKeyguardViewMediator).onWake(true /* withUnlock */);
+ verify(mKeyguardViewMediator).onWakeAndUnlocking();
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
}
@Test
- public void onBiometricAuthenticated_whenFingerprintAndInteractive_wakeWithoutUnlock() {
- reset(mUpdateMonitor);
- reset(mStatusBarKeyguardViewManager);
- when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
- when(mDozeScrimController.isPulsing()).thenReturn(false);
- // the value of isStrongBiometric doesn't matter here since we only care about the returned
- // value of isUnlockingWithBiometricAllowed()
- mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
-
- verify(mKeyguardViewMediator).onWake(false /* withUnlock */);
- assertThat(mBiometricUnlockController.getMode())
- .isEqualTo(BiometricUnlockController.MODE_ONLY_WAKE);
- }
-
- @Test
public void onBiometricAuthenticated_whenFingerprint_notifyKeyguardAuthenticated() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index 5a1a1ae..c2c630e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -167,8 +167,6 @@
final Window window = mDialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.setDimAmount(0.6f);
window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 078a1d9..677871f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -347,9 +347,7 @@
final Window window = mDialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_DIM_BEHIND
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+ | WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.setDimAmount(0.6f);
window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 1af35af..ca7fe0c 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -2359,13 +2359,37 @@
}
} while (headBusy);
+ if (runBackup) {
+ CountDownLatch latch = new CountDownLatch(1);
+ String[] pkg = new String[]{entry.packageName};
+ try {
+ mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
+ this,
+ mOperationStorage,
+ /* observer */ null,
+ pkg,
+ /* updateSchedule */ true,
+ scheduledJob,
+ latch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ "BMS.beginFullBackup()",
+ getEligibilityRulesForOperation(OperationType.BACKUP));
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "Failed to start backup", e);
+ runBackup = false;
+ }
+ }
+
if (!runBackup) {
if (DEBUG_SCHEDULING) {
Slog.i(
TAG,
addUserIdToLogMessage(
mUserId,
- "Nothing pending full backup; rescheduling +" + latency));
+ "Nothing pending full backup or failed to start the "
+ + "operation; rescheduling +" + latency));
}
final long deferTime = latency; // pin for the closure
FullBackupJob.schedule(mUserId, mContext, deferTime, mConstants);
@@ -2374,21 +2398,6 @@
// Okay, the top thing is ready for backup now. Do it.
mFullBackupQueue.remove(0);
- CountDownLatch latch = new CountDownLatch(1);
- String[] pkg = new String[]{entry.packageName};
- mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
- this,
- mOperationStorage,
- /* observer */ null,
- pkg,
- /* updateSchedule */ true,
- scheduledJob,
- latch,
- /* backupObserver */ null,
- /* monitor */ null,
- /* userInitiated */ false,
- "BMS.beginFullBackup()",
- getEligibilityRulesForOperation(OperationType.BACKUP));
// Acquiring wakelock for PerformFullTransportBackupTask before its start.
mWakelock.acquire();
(new Thread(mRunningFullBackupTask)).start();
@@ -2884,7 +2893,6 @@
public void fullTransportBackup(String[] pkgNames) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
"fullTransportBackup");
-
final int callingUserHandle = UserHandle.getCallingUserId();
// TODO: http://b/22388012
if (callingUserHandle != UserHandle.USER_SYSTEM) {
@@ -2936,6 +2944,9 @@
for (String pkg : pkgNames) {
enqueueFullBackup(pkg, now);
}
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "Failed to start backup: ", e);
+ return;
} finally {
Binder.restoreCallingIdentity(oldId);
}
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 e74a3b9..eac98f2 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -99,6 +99,9 @@
* mBackupRunner.getBackupResultBlocking().
*/
public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
+ /**
+ * @throws IllegalStateException if there's no transport available.
+ */
public static PerformFullTransportBackupTask newWithCurrentTransport(
UserBackupManagerService backupManagerService,
OperationStorage operationStorage,
@@ -115,6 +118,9 @@
TransportManager transportManager = backupManagerService.getTransportManager();
TransportConnection transportConnection = transportManager.getCurrentTransportClient(
caller);
+ if (transportConnection == null) {
+ throw new IllegalStateException("No TransportConnection available");
+ }
OnTaskFinishedListener listener =
listenerCaller ->
transportManager.disposeOfTransportClient(transportConnection,
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
index 9bb1fb9..a288f7b 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -119,13 +119,23 @@
}
}
+ // This method is only called when app is force-closed via settings,
+ // but does not handle crashes. Binder death should be handled in #binderDied()
@Override
public void onBindingDied(@NonNull ComponentName name) {
- // IMPORTANT: call super!
+ // IMPORTANT: call super! this will also invoke binderDied()
super.onBindingDied(name);
if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString());
+ }
+ @Override
+ public void binderDied() {
+ super.binderDied();
+
+ if (DEBUG) Log.d(TAG, "binderDied() " + mComponentName.toShortString());
+
+ // Handle primary process being killed
if (mListener != null) {
mListener.onBindingDied(mUserId, mComponentName.getPackageName());
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 0b43744..43e2b88 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -16,6 +16,8 @@
package com.android.server.companion.virtual;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -24,6 +26,7 @@
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
+import android.companion.AssociationRequest;
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.VirtualDeviceParams.ActivityPolicy;
@@ -45,7 +48,6 @@
import java.util.List;
import java.util.Set;
-import java.util.function.Consumer;
/**
@@ -63,6 +65,14 @@
void onRunningAppsChanged(ArraySet<Integer> runningUids);
}
+ /**
+ * For communicating when activities are blocked from running on the display by this policy
+ * controller.
+ */
+ public interface ActivityBlockedCallback {
+ /** Called when an activity is blocked.*/
+ void onActivityBlocked(int displayId, ActivityInfo activityInfo);
+ }
private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
new ComponentName("android", BlockedAppStreamingActivity.class.getName());
@@ -86,7 +96,8 @@
private final Object mGenericWindowPolicyControllerLock = new Object();
@ActivityPolicy
private final int mDefaultActivityPolicy;
- private final Consumer<ActivityInfo> mActivityBlockedCallback;
+ private final ActivityBlockedCallback mActivityBlockedCallback;
+ private int mDisplayId = Display.INVALID_DISPLAY;
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
@@ -95,6 +106,8 @@
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListener =
new ArraySet<>();
+ @Nullable
+ private final @AssociationRequest.DeviceProfile String mDeviceProfile;
/**
* Creates a window policy controller that is generic to the different use cases of virtual
@@ -115,10 +128,10 @@
* {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_ALLOWED}
* @param defaultActivityPolicy Whether activities are default allowed to be displayed or
* blocked.
- * @param activityListener Activity listener to listen for activity changes. The display ID
- * is not populated in this callback and is always {@link Display#INVALID_DISPLAY}.
+ * @param activityListener Activity listener to listen for activity changes.
* @param activityBlockedCallback Callback that is called when an activity is blocked from
* launching.
+ * @param deviceProfile The {@link AssociationRequest.DeviceProfile} of this virtual device.
*/
public GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
@NonNull ArraySet<UserHandle> allowedUsers,
@@ -128,7 +141,8 @@
@NonNull Set<ComponentName> blockedActivities,
@ActivityPolicy int defaultActivityPolicy,
@NonNull ActivityListener activityListener,
- @NonNull Consumer<ActivityInfo> activityBlockedCallback) {
+ @NonNull ActivityBlockedCallback activityBlockedCallback,
+ @AssociationRequest.DeviceProfile String deviceProfile) {
super();
mAllowedUsers = allowedUsers;
mAllowedCrossTaskNavigations = new ArraySet<>(allowedCrossTaskNavigations);
@@ -139,6 +153,14 @@
mActivityBlockedCallback = activityBlockedCallback;
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
+ mDeviceProfile = deviceProfile;
+ }
+
+ /**
+ * Expected to be called once this object is associated with a newly created display.
+ */
+ public void setDisplayId(int displayId) {
+ mDisplayId = displayId;
}
/** Register a listener for running applications changes. */
@@ -162,7 +184,7 @@
for (int i = 0; i < activityCount; i++) {
final ActivityInfo aInfo = activities.get(i);
if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) {
- mActivityBlockedCallback.accept(aInfo);
+ mActivityBlockedCallback.onActivityBlocked(mDisplayId, aInfo);
return false;
}
}
@@ -184,7 +206,7 @@
}
if (!canContainActivity(activityInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) {
- mActivityBlockedCallback.accept(activityInfo);
+ mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
return false;
}
@@ -194,14 +216,14 @@
if (isNewTask && !mBlockedCrossTaskNavigations.isEmpty()
&& mBlockedCrossTaskNavigations.contains(activityComponent)) {
Slog.d(TAG, "Virtual device blocking cross task navigation of " + activityComponent);
- mActivityBlockedCallback.accept(activityInfo);
+ mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
return false;
}
if (isNewTask && !mAllowedCrossTaskNavigations.isEmpty()
&& !mAllowedCrossTaskNavigations.contains(activityComponent)) {
Slog.d(TAG, "Virtual device not allowing cross task navigation of "
+ activityComponent);
- mActivityBlockedCallback.accept(activityInfo);
+ mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
return false;
}
@@ -213,7 +235,7 @@
public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
int systemWindowFlags) {
if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) {
- mActivityBlockedCallback.accept(activityInfo);
+ mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
return false;
}
return true;
@@ -227,7 +249,7 @@
if (mActivityListener != null && topActivity != null) {
// Post callback on the main thread so it doesn't block activity launching
mHandler.post(() ->
- mActivityListener.onTopActivityChanged(Display.INVALID_DISPLAY, topActivity));
+ mActivityListener.onTopActivityChanged(mDisplayId, topActivity));
}
}
@@ -238,7 +260,7 @@
mRunningUids.addAll(runningUids);
if (mActivityListener != null && mRunningUids.isEmpty()) {
// Post callback on the main thread so it doesn't block activity launching
- mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY));
+ mHandler.post(() -> mActivityListener.onDisplayEmpty(mDisplayId));
}
}
mHandler.post(() -> {
@@ -248,6 +270,21 @@
});
}
+ @Override
+ public boolean canShowTasksInRecents() {
+ if (mDeviceProfile == null) {
+ return true;
+ }
+ // TODO(b/234075973) : Remove this once proper API is ready.
+ switch (mDeviceProfile) {
+ case DEVICE_PROFILE_AUTOMOTIVE_PROJECTION:
+ return false;
+ case DEVICE_PROFILE_APP_STREAMING:
+ default:
+ return true;
+ }
+ }
+
/**
* Returns true if an app with the given UID has an activity running on the virtual display for
* this controller.
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 638b3ae..3b3bf11 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -64,7 +64,6 @@
import android.view.Display;
import android.view.WindowManager;
import android.widget.Toast;
-import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -110,11 +109,11 @@
@GuardedBy("mVirtualDeviceLock")
private boolean mDefaultShowPointerIcon = true;
- private ActivityListener createListenerAdapter(int displayId) {
+ private ActivityListener createListenerAdapter() {
return new ActivityListener() {
@Override
- public void onTopActivityChanged(int unusedDisplayId, ComponentName topActivity) {
+ public void onTopActivityChanged(int displayId, ComponentName topActivity) {
try {
mActivityListener.onTopActivityChanged(displayId, topActivity);
} catch (RemoteException e) {
@@ -123,7 +122,7 @@
}
@Override
- public void onDisplayEmpty(int unusedDisplayId) {
+ public void onDisplayEmpty(int displayId) {
try {
mActivityListener.onDisplayEmpty(displayId);
} catch (RemoteException e) {
@@ -529,24 +528,8 @@
mInputController.dump(fout);
}
- DisplayWindowPolicyController onVirtualDisplayCreatedLocked(int displayId) {
+ GenericWindowPolicyController createWindowPolicyController() {
synchronized (mVirtualDeviceLock) {
- if (mVirtualDisplayIds.contains(displayId)) {
- throw new IllegalStateException(
- "Virtual device already have a virtual display with ID " + displayId);
- }
- mVirtualDisplayIds.add(displayId);
- mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
- mInputController.setPointerAcceleration(1f, displayId);
- mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
- displayId);
- mInputController.setLocalIme(displayId);
-
- // Since we're being called in the middle of the display being created, we post a
- // task to grab the wakelock instead of doing it synchronously here, to avoid
- // reentrancy problems.
- mContext.getMainThreadHandler().post(() -> addWakeLockForDisplay(displayId));
-
final GenericWindowPolicyController gwpc =
new GenericWindowPolicyController(FLAG_SECURE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
@@ -556,18 +539,36 @@
mParams.getAllowedActivities(),
mParams.getBlockedActivities(),
mParams.getDefaultActivityPolicy(),
- createListenerAdapter(displayId),
- activityInfo -> onActivityBlocked(displayId, activityInfo));
+ createListenerAdapter(),
+ this::onActivityBlocked,
+ mAssociationInfo.getDeviceProfile());
gwpc.registerRunningAppsChangedListener(/* listener= */ this);
- mWindowPolicyControllers.put(displayId, gwpc);
return gwpc;
}
}
- void addWakeLockForDisplay(int displayId) {
+ void onVirtualDisplayCreatedLocked(GenericWindowPolicyController gwpc, int displayId) {
synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplayIds.contains(displayId)
- || mPerDisplayWakelocks.containsKey(displayId)) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ return;
+ }
+ if (mVirtualDisplayIds.contains(displayId)) {
+ throw new IllegalStateException(
+ "Virtual device already has a virtual display with ID " + displayId);
+ }
+ mVirtualDisplayIds.add(displayId);
+
+ gwpc.setDisplayId(displayId);
+ mWindowPolicyControllers.put(displayId, gwpc);
+
+ mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
+ mInputController.setPointerAcceleration(1f, displayId);
+ mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
+ displayId);
+ mInputController.setLocalIme(displayId);
+
+
+ if (mPerDisplayWakelocks.containsKey(displayId)) {
Slog.e(TAG, "Not creating wakelock for displayId " + displayId);
return;
}
@@ -575,8 +576,8 @@
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
TAG + ":" + displayId, displayId);
- wakeLock.acquire();
mPerDisplayWakelocks.put(displayId, wakeLock);
+ wakeLock.acquire();
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 6398b21..35e9060 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -31,6 +31,10 @@
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.content.Context;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -41,9 +45,9 @@
import android.util.Slog;
import android.util.SparseArray;
import android.widget.Toast;
-import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceImpl.PendingTrampoline;
@@ -203,6 +207,7 @@
}
}
+ @VisibleForTesting
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
VirtualDeviceImpl.PendingTrampolineCallback {
@@ -265,6 +270,50 @@
}
}
+ @Override // Binder call
+ public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
+ IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName)
+ throws RemoteException {
+ final int callingUid = getCallingUid();
+ if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
+ throw new SecurityException(
+ "Package name " + packageName + " does not belong to calling uid "
+ + callingUid);
+ }
+ VirtualDeviceImpl virtualDeviceImpl;
+ synchronized (mVirtualDeviceManagerLock) {
+ virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getAssociationId());
+ if (virtualDeviceImpl == null) {
+ throw new SecurityException("Invalid VirtualDevice");
+ }
+ }
+ if (virtualDeviceImpl.getOwnerUid() != callingUid) {
+ throw new SecurityException(
+ "uid " + callingUid
+ + " is not the owner of the supplied VirtualDevice");
+ }
+ GenericWindowPolicyController gwpc;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ gwpc = virtualDeviceImpl.createWindowPolicyController();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ DisplayManagerInternal displayManager = getLocalService(
+ DisplayManagerInternal.class);
+ int displayId = displayManager.createVirtualDisplay(virtualDisplayConfig, callback,
+ virtualDevice, gwpc, packageName);
+
+ final long tokenTwo = Binder.clearCallingIdentity();
+ try {
+ virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId);
+ return displayId;
+ } finally {
+ Binder.restoreCallingIdentity(tokenTwo);
+ }
+ }
+
@Nullable
private AssociationInfo getAssociationInfo(String packageName, int associationId) {
final int callingUserId = getCallingUserHandle().getIdentifier();
@@ -337,14 +386,6 @@
}
@Override
- public DisplayWindowPolicyController onVirtualDisplayCreated(IVirtualDevice virtualDevice,
- int displayId) {
- synchronized (mVirtualDeviceManagerLock) {
- return ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayCreatedLocked(displayId);
- }
- }
-
- @Override
public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
synchronized (mVirtualDeviceManagerLock) {
((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index ad6e7db..ff1a495 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -223,7 +223,7 @@
@Override // from AbstractMasterSystemService
protected ContentCapturePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
- return new ContentCapturePerUserService(this, mLock, disabled, resolvedUserId, mHandler);
+ return new ContentCapturePerUserService(this, mLock, disabled, resolvedUserId);
}
@Override // from SystemService
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 9bc1cee..41a7592 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -46,7 +46,6 @@
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -76,7 +75,6 @@
import com.android.server.infra.AbstractPerUserSystemService;
import java.io.PrintWriter;
-import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -90,10 +88,6 @@
private static final String TAG = ContentCapturePerUserService.class.getSimpleName();
- private static final int MAX_REBIND_COUNTS = 5;
- // 5 minutes
- private static final long REBIND_DURATION_MS = 5 * 60 * 1_000;
-
@GuardedBy("mLock")
private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>();
@@ -127,18 +121,11 @@
@GuardedBy("mLock")
private ContentCaptureServiceInfo mInfo;
- private Instant mLastRebindTime;
- private int mRebindCount;
- private final Handler mHandler;
-
- private final Runnable mReBindServiceRunnable = new RebindServiceRunnable();
-
// TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
- @NonNull Object lock, boolean disabled, @UserIdInt int userId, Handler handler) {
+ @NonNull Object lock, boolean disabled, @UserIdInt int userId) {
super(master, lock, userId);
- mHandler = handler;
updateRemoteServiceLocked(disabled);
}
@@ -203,43 +190,9 @@
Slog.w(TAG, "remote service died: " + service);
synchronized (mLock) {
mZombie = true;
- // Reset rebindCount if over 12 hours mLastRebindTime
- if (mLastRebindTime != null && Instant.now().isAfter(
- mLastRebindTime.plusMillis(12 * 60 * 60 * 1000))) {
- if (mMaster.debug) {
- Slog.i(TAG, "The current rebind count " + mRebindCount + " is reset.");
- }
- mRebindCount = 0;
- }
- if (mRebindCount >= MAX_REBIND_COUNTS) {
- writeServiceEvent(
- FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED,
- getServiceComponentName());
- }
- if (mRebindCount < MAX_REBIND_COUNTS) {
- mHandler.removeCallbacks(mReBindServiceRunnable);
- mHandler.postDelayed(mReBindServiceRunnable, REBIND_DURATION_MS);
- }
- }
- }
-
- private void updateRemoteServiceAndResurrectSessionsLocked() {
- boolean disabled = !isEnabledLocked();
- updateRemoteServiceLocked(disabled);
- resurrectSessionsLocked();
- }
-
- private final class RebindServiceRunnable implements Runnable{
-
- @Override
- public void run() {
- synchronized (mLock) {
- if (mZombie) {
- mLastRebindTime = Instant.now();
- mRebindCount++;
- updateRemoteServiceAndResurrectSessionsLocked();
- }
- }
+ writeServiceEvent(
+ FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED,
+ getServiceComponentName());
}
}
@@ -287,8 +240,8 @@
}
void onPackageUpdatedLocked() {
- mRebindCount = 0;
- updateRemoteServiceAndResurrectSessionsLocked();
+ updateRemoteServiceLocked(!isEnabledLocked());
+ resurrectSessionsLocked();
}
@GuardedBy("mLock")
@@ -602,8 +555,6 @@
mInfo.dump(prefix2, pw);
}
pw.print(prefix); pw.print("Zombie: "); pw.println(mZombie);
- pw.print(prefix); pw.print("Rebind count: "); pw.println(mRebindCount);
- pw.print(prefix); pw.print("Last rebind: "); pw.println(mLastRebindTime);
if (mRemoteService != null) {
pw.print(prefix); pw.println("remote service:");
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 24e5de9..5c84a62 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -29,7 +29,12 @@
"file_patterns": ["SensorPrivacyService\\.java"]
},
{
- "name": "BinaryTransparencyServiceTest",
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.BinaryTransparencyServiceTest"
+ }
+ ],
"file_patterns": ["BinaryTransparencyService\\.java"]
}
],
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 7a52af6..0807fba 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2355,9 +2355,8 @@
return;
}
- if (VDBG) {
- log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId);
- }
+ log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId);
+ mLocalLog.log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId);
mActiveDataSubId = activeDataSubId;
synchronized (mRecords) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a09174b..38d0e30 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4177,8 +4177,7 @@
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
- HostingRecord hostingRecord = new HostingRecord(
- HostingRecord.HOSTING_TYPE_SERVICE, r.instanceName,
+ HostingRecord hostingRecord = new HostingRecord("service", r.instanceName,
r.definingPackageName, r.definingUid, r.serviceInfo.processName);
ProcessRecord app;
@@ -5184,7 +5183,7 @@
}
boolean bringDownDisabledPackageServicesLocked(String packageName, Set<String> filterByClasses,
- int userId, boolean evenPersistent, boolean doit) {
+ int userId, boolean evenPersistent, boolean fullStop, boolean doit) {
boolean didSomething = false;
if (mTmpCollectionResults != null) {
@@ -5222,6 +5221,18 @@
if (size > 0) {
mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
+ if (fullStop && !mTmpCollectionResults.isEmpty()) {
+ // if we're tearing down the app's entire service state, account for possible
+ // races around FGS notifications by explicitly tidying up in a separate
+ // pass post-shutdown
+ final ArrayList<ServiceRecord> allServices =
+ (ArrayList<ServiceRecord>) mTmpCollectionResults.clone();
+ mAm.mHandler.postDelayed(() -> {
+ for (int i = 0; i < allServices.size(); i++) {
+ allServices.get(i).cancelNotification();
+ }
+ }, 250L);
+ }
mTmpCollectionResults.clear();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c946888..e639b29 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1876,7 +1876,7 @@
false,
0,
null,
- new HostingRecord(HostingRecord.HOSTING_TYPE_SYSTEM));
+ new HostingRecord("system"));
app.setPersistent(true);
app.setPid(MY_PID);
app.mState.setMaxAdj(ProcessList.SYSTEM_ADJ);
@@ -4370,7 +4370,8 @@
// Clean-up disabled services.
mServices.bringDownDisabledPackageServicesLocked(
- packageName, disabledClasses, userId, false /* evenPersistent */, true /* doIt */);
+ packageName, disabledClasses, userId, false /* evenPersistent */,
+ false /* fullStop */, true /* doIt */);
// Clean-up disabled providers.
ArrayList<ContentProviderRecord> providers = new ArrayList<>();
@@ -4445,7 +4446,7 @@
}
mServices.bringDownDisabledPackageServicesLocked(
- packageName, null, userId, false, true);
+ packageName, null, userId, false, true, true);
if (mBooted) {
mAtmInternal.resumeTopActivities(true);
@@ -4498,7 +4499,7 @@
}
if (mServices.bringDownDisabledPackageServicesLocked(
- packageName, null /* filterByClasses */, userId, evenPersistent, doit)) {
+ packageName, null /* filterByClasses */, userId, evenPersistent, true, doit)) {
if (!doit) {
return true;
}
@@ -4730,7 +4731,7 @@
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
mProcessList.startProcessLocked(app,
- new HostingRecord(HostingRecord.HOSTING_TYPE_LINK_FAIL, processName),
+ new HostingRecord("link fail", processName),
ZYGOTE_POLICY_FLAG_EMPTY);
return false;
}
@@ -4999,17 +5000,6 @@
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
}
-
- final HostingRecord hostingRecord = app.getHostingRecord();
- final String action = hostingRecord.getAction();
- String shortAction = action;
- if (action != null) {
- // only log the last part of the action string to save stats data.
- int index = action.lastIndexOf(".");
- if (index != -1 && index != action.length() - 1) {
- shortAction = action.substring(index + 1);
- }
- }
FrameworkStatsLog.write(
FrameworkStatsLog.PROCESS_START_TIME,
app.info.uid,
@@ -5019,10 +5009,8 @@
app.getStartElapsedTime(),
(int) (bindApplicationTimeMillis - app.getStartUptime()),
(int) (SystemClock.uptimeMillis() - app.getStartUptime()),
- hostingRecord.getType(),
- hostingRecord.getName(),
- shortAction,
- HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()));
+ app.getHostingRecord().getType(),
+ (app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : ""));
return true;
}
@@ -5121,7 +5109,7 @@
Slog.v(TAG_PROCESSES, "Starting process on hold: " + procs.get(ip));
}
mProcessList.startProcessLocked(procs.get(ip),
- new HostingRecord(HostingRecord.HOSTING_TYPE_ON_HOLD),
+ new HostingRecord("on-hold"),
ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
}
}
@@ -6712,7 +6700,7 @@
isSdkSandbox,
sdkSandboxUid,
sdkSandboxClientAppPackage,
- new HostingRecord(HostingRecord.HOSTING_TYPE_ADDED_APPLICATION,
+ new HostingRecord("added application",
customProcess != null ? customProcess : info.processName));
updateLruProcessLocked(app, false, null);
updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
@@ -6741,8 +6729,7 @@
}
if (app.getThread() == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
- mProcessList.startProcessLocked(app, new HostingRecord(
- HostingRecord.HOSTING_TYPE_ADDED_APPLICATION,
+ mProcessList.startProcessLocked(app, new HostingRecord("added application",
customProcess != null ? customProcess : app.processName),
zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,
abiOverride);
@@ -12397,8 +12384,7 @@
mProcessList.addProcessNameLocked(app);
app.setPendingStart(false);
- mProcessList.startProcessLocked(app, new HostingRecord(
- HostingRecord.HOSTING_TYPE_RESTART, app.processName),
+ mProcessList.startProcessLocked(app, new HostingRecord("restart", app.processName),
ZYGOTE_POLICY_FLAG_EMPTY);
return true;
} else if (pid > 0 && pid != MY_PID) {
@@ -12783,7 +12769,7 @@
// startProcessLocked() returns existing proc's record if it's already running
ProcessRecord proc = startProcessLocked(app.processName, app,
false, 0,
- new HostingRecord(HostingRecord.HOSTING_TYPE_BACKUP, hostingName),
+ new HostingRecord("backup", hostingName),
ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false);
if (proc == null) {
Slog.e(TAG, "Unable to start backup agent process " + r);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index ed492bc..08c1de6 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -836,12 +836,18 @@
report.type = ApplicationErrorReport.TYPE_CRASH;
report.crashInfo = crashInfo;
} else if (errState.isNotResponding()) {
+ final ActivityManager.ProcessErrorStateInfo anrReport =
+ errState.getNotRespondingReport();
+ if (anrReport == null) {
+ // The ANR dump is still ongoing, ignore it for now.
+ return null;
+ }
report.type = ApplicationErrorReport.TYPE_ANR;
report.anrInfo = new ApplicationErrorReport.AnrInfo();
- report.anrInfo.activity = errState.getNotRespondingReport().tag;
- report.anrInfo.cause = errState.getNotRespondingReport().shortMsg;
- report.anrInfo.info = errState.getNotRespondingReport().longMsg;
+ report.anrInfo.activity = anrReport.tag;
+ report.anrInfo.cause = anrReport.shortMsg;
+ report.anrInfo.info = anrReport.longMsg;
}
return report;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index f7aa7c15..d2e40c5 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1870,9 +1870,8 @@
r.curApp = mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
- r.intent.getAction()),
- isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
+ new HostingRecord("broadcast", r.curComponent), isActivityCapable
+ ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
if (r.curApp == null) {
// Ah, this recipient is unavailable. Finish it if necessary,
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 728792f..7289331 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -483,7 +483,7 @@
checkTime(startTime, "getContentProviderImpl: before start process");
proc = mService.startProcessLocked(
cpi.processName, cpr.appInfo, false, 0,
- new HostingRecord(HostingRecord.HOSTING_TYPE_CONTENT_PROVIDER,
+ new HostingRecord("content provider",
new ComponentName(
cpi.applicationInfo.packageName, cpi.name)),
Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);
diff --git a/services/core/java/com/android/server/am/HostingRecord.java b/services/core/java/com/android/server/am/HostingRecord.java
index c41dc32..bbf5861 100644
--- a/services/core/java/com/android/server/am/HostingRecord.java
+++ b/services/core/java/com/android/server/am/HostingRecord.java
@@ -16,9 +16,7 @@
package com.android.server.am;
-import android.annotation.Nullable;
import android.content.ComponentName;
-import android.os.ProcessStartTime;
/**
* This class describes various information required to start a process.
@@ -45,9 +43,6 @@
*
* {@code mIsTopApp} will be passed to {@link android.os.Process#start}. So Zygote will initialize
* the process with high priority.
- *
- * {@code mAction} the broadcast's intent action if the process is started for a broadcast
- * receiver.
*/
public final class HostingRecord {
@@ -55,21 +50,6 @@
private static final int WEBVIEW_ZYGOTE = 1;
private static final int APP_ZYGOTE = 2;
- public static final String HOSTING_TYPE_ACTIVITY = "activity";
- public static final String HOSTING_TYPE_ADDED_APPLICATION = "added application";
- public static final String HOSTING_TYPE_BACKUP = "backup";
- public static final String HOSTING_TYPE_BROADCAST = "broadcast";
- public static final String HOSTING_TYPE_CONTENT_PROVIDER = "content provider";
- public static final String HOSTING_TYPE_LINK_FAIL = "link fail";
- public static final String HOSTING_TYPE_ON_HOLD = "on-hold";
- public static final String HOSTING_TYPE_NEXT_ACTIVITY = "next-activity";
- public static final String HOSTING_TYPE_NEXT_TOP_ACTIVITY = "next-top-activity";
- public static final String HOSTING_TYPE_RESTART = "restart";
- public static final String HOSTING_TYPE_SERVICE = "service";
- public static final String HOSTING_TYPE_SYSTEM = "system";
- public static final String HOSTING_TYPE_TOP_ACTIVITY = "top-activity";
- public static final String HOSTING_TYPE_EMPTY = "";
-
private final String mHostingType;
private final String mHostingName;
private final int mHostingZygote;
@@ -77,34 +57,26 @@
private final int mDefiningUid;
private final boolean mIsTopApp;
private final String mDefiningProcessName;
- @Nullable private final String mAction;
public HostingRecord(String hostingType) {
this(hostingType, null /* hostingName */, REGULAR_ZYGOTE, null /* definingPackageName */,
- -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */,
- null /* action */);
+ -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */);
}
public HostingRecord(String hostingType, ComponentName hostingName) {
this(hostingType, hostingName, REGULAR_ZYGOTE);
}
- public HostingRecord(String hostingType, ComponentName hostingName, @Nullable String action) {
- this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
- null /* definingPackageName */, -1 /* mDefiningUid */, false /* isTopApp */,
- null /* definingProcessName */, action);
- }
-
public HostingRecord(String hostingType, ComponentName hostingName, String definingPackageName,
int definingUid, String definingProcessName) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, definingPackageName,
- definingUid, false /* isTopApp */, definingProcessName, null /* action */);
+ definingUid, false /* isTopApp */, definingProcessName);
}
public HostingRecord(String hostingType, ComponentName hostingName, boolean isTopApp) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */,
- null /* definingProcessName */, null /* action */);
+ null /* definingProcessName */);
}
public HostingRecord(String hostingType, String hostingName) {
@@ -117,13 +89,12 @@
private HostingRecord(String hostingType, String hostingName, int hostingZygote) {
this(hostingType, hostingName, hostingZygote, null /* definingPackageName */,
- -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */,
- null /* action */);
+ -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */);
}
private HostingRecord(String hostingType, String hostingName, int hostingZygote,
String definingPackageName, int definingUid, boolean isTopApp,
- String definingProcessName, @Nullable String action) {
+ String definingProcessName) {
mHostingType = hostingType;
mHostingName = hostingName;
mHostingZygote = hostingZygote;
@@ -131,7 +102,6 @@
mDefiningUid = definingUid;
mIsTopApp = isTopApp;
mDefiningProcessName = definingProcessName;
- mAction = action;
}
public String getType() {
@@ -177,24 +147,14 @@
}
/**
- * Returns the broadcast's intent action if the process is started for a broadcast receiver.
- *
- * @return the intent action of the broadcast.
- */
- public @Nullable String getAction() {
- return mAction;
- }
-
- /**
* Creates a HostingRecord for a process that must spawn from the webview zygote
* @param hostingName name of the component to be hosted in this process
* @return The constructed HostingRecord
*/
public static HostingRecord byWebviewZygote(ComponentName hostingName,
String definingPackageName, int definingUid, String definingProcessName) {
- return new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY, hostingName.toShortString(),
- WEBVIEW_ZYGOTE, definingPackageName, definingUid, false /* isTopApp */,
- definingProcessName, null /* action */);
+ return new HostingRecord("", hostingName.toShortString(), WEBVIEW_ZYGOTE,
+ definingPackageName, definingUid, false /* isTopApp */, definingProcessName);
}
/**
@@ -206,9 +166,8 @@
*/
public static HostingRecord byAppZygote(ComponentName hostingName, String definingPackageName,
int definingUid, String definingProcessName) {
- return new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY, hostingName.toShortString(),
- APP_ZYGOTE, definingPackageName, definingUid, false /* isTopApp */,
- definingProcessName, null /* action */);
+ return new HostingRecord("", hostingName.toShortString(), APP_ZYGOTE,
+ definingPackageName, definingUid, false /* isTopApp */, definingProcessName);
}
/**
@@ -224,44 +183,4 @@
public boolean usesWebviewZygote() {
return mHostingZygote == WEBVIEW_ZYGOTE;
}
-
- /**
- * Map the string hostingType to enum HostingType defined in ProcessStartTime proto.
- * @param hostingType
- * @return enum HostingType defined in ProcessStartTime proto
- */
- public static int getHostingTypeIdStatsd(String hostingType) {
- switch(hostingType) {
- case HOSTING_TYPE_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_ACTIVITY;
- case HOSTING_TYPE_ADDED_APPLICATION:
- return ProcessStartTime.HOSTING_TYPE_ADDED_APPLICATION;
- case HOSTING_TYPE_BACKUP:
- return ProcessStartTime.HOSTING_TYPE_BACKUP;
- case HOSTING_TYPE_BROADCAST:
- return ProcessStartTime.HOSTING_TYPE_BROADCAST;
- case HOSTING_TYPE_CONTENT_PROVIDER:
- return ProcessStartTime.HOSTING_TYPE_CONTENT_PROVIDER;
- case HOSTING_TYPE_LINK_FAIL:
- return ProcessStartTime.HOSTING_TYPE_LINK_FAIL;
- case HOSTING_TYPE_ON_HOLD:
- return ProcessStartTime.HOSTING_TYPE_ON_HOLD;
- case HOSTING_TYPE_NEXT_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_NEXT_ACTIVITY;
- case HOSTING_TYPE_NEXT_TOP_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_NEXT_TOP_ACTIVITY;
- case HOSTING_TYPE_RESTART:
- return ProcessStartTime.HOSTING_TYPE_RESTART;
- case HOSTING_TYPE_SERVICE:
- return ProcessStartTime.HOSTING_TYPE_SERVICE;
- case HOSTING_TYPE_SYSTEM:
- return ProcessStartTime.HOSTING_TYPE_SYSTEM;
- case HOSTING_TYPE_TOP_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_TOP_ACTIVITY;
- case HOSTING_TYPE_EMPTY:
- return ProcessStartTime.HOSTING_TYPE_EMPTY;
- default:
- return ProcessStartTime.HOSTING_TYPE_UNKNOWN;
- }
- }
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 81a8680..4044cce 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -310,6 +310,17 @@
requiredPermission, null, null, 0, 0, 0, options);
}
+ /**
+ * Return true if the activity options allows PendingIntent to use caller's BAL permission.
+ */
+ public static boolean isPendingIntentBalAllowedByPermission(
+ @Nullable ActivityOptions activityOptions) {
+ if (activityOptions == null) {
+ return false;
+ }
+ return activityOptions.isPendingIntentBackgroundActivityLaunchAllowedByPermission();
+ }
+
public static boolean isPendingIntentBalAllowedByCaller(
@Nullable ActivityOptions activityOptions) {
if (activityOptions == null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f0fe2dd..02648c4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9171,7 +9171,7 @@
throw new IllegalArgumentException("Invalid timeOutMs/usagesToMute");
}
Log.i(TAG, "muteAwaitConnection dev:" + device + " timeOutMs:" + timeOutMs
- + " usages:" + usages);
+ + " usages:" + Arrays.toString(usages));
if (mDeviceBroker.isDeviceConnected(device)) {
// not throwing an exception as there could be a race between a connection (server-side,
@@ -9223,7 +9223,7 @@
mutedUsages = mMutedUsagesAwaitingConnection;
mMutingExpectedDevice = null;
mMutedUsagesAwaitingConnection = null;
- mPlaybackMonitor.cancelMuteAwaitConnection();
+ mPlaybackMonitor.cancelMuteAwaitConnection("cancelMuteAwaitConnection dev:" + device);
}
dispatchMuteAwaitConnection(cb -> { try { cb.dispatchOnUnmutedEvent(
AudioManager.MuteAwaitConnectionCallback.EVENT_CANCEL, device, mutedUsages);
@@ -9259,8 +9259,8 @@
}
mMutingExpectedDevice = null;
mMutedUsagesAwaitingConnection = null;
- Log.i(TAG, "muteAwaitConnection device " + device + " connected, unmuting");
- mPlaybackMonitor.cancelMuteAwaitConnection();
+ mPlaybackMonitor.cancelMuteAwaitConnection(
+ "checkMuteAwaitConnection device " + device + " connected, unmuting");
}
dispatchMuteAwaitConnection(cb -> { try { cb.dispatchOnUnmutedEvent(
AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION, device, mutedUsages);
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index a4468a3..b3e7e31 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -1170,8 +1170,8 @@
}
}
- void cancelMuteAwaitConnection() {
- sEventLogger.loglogi("cancelMuteAwaitConnection()", TAG);
+ void cancelMuteAwaitConnection(String source) {
+ sEventLogger.loglogi("cancelMuteAwaitConnection() from:" + source, TAG);
synchronized (mPlayerLock) {
// cancel scheduled timeout, ignore device, only one expected device at a time
mEventHandler.removeMessages(MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION);
@@ -1223,7 +1223,7 @@
+ " uid:" + apc.getClientUid())).printLog(TAG));
apc.getPlayerProxy().applyVolumeShaper(
MUTE_AWAIT_CONNECTION_VSHAPE,
- PLAY_CREATE_IF_NEEDED);
+ PLAY_SKIP_RAMP);
mMutedPlayersAwaitingConnection.add(apc.getPlayerInterfaceId());
} catch (Exception e) {
Log.e(TAG, "awaiting connection: error muting player "
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 94d3d15..f2b4d42 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -17,7 +17,6 @@
package com.android.server.companion.virtual;
import android.companion.virtual.IVirtualDevice;
-import android.window.DisplayWindowPolicyController;
/**
* Virtual device manager local service interface.
@@ -31,17 +30,6 @@
public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
/**
- * Notify a virtual display is created.
- *
- * @param virtualDevice The virtual device where the virtual display located.
- * @param displayId The display id of the created virtual display.
- *
- * @return The {@link DisplayWindowPolicyController} of the virtual device.
- */
- public abstract DisplayWindowPolicyController onVirtualDisplayCreated(
- IVirtualDevice virtualDevice, int displayId);
-
- /**
* Notify a virtual display is removed.
*
* @param virtualDevice The virtual device where the virtual display located.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e776936..ec7ccc4 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1231,7 +1231,7 @@
private int createVirtualDisplayInternal(VirtualDisplayConfig virtualDisplayConfig,
IVirtualDisplayCallback callback, IMediaProjection projection,
- IVirtualDevice virtualDevice, String packageName) {
+ IVirtualDevice virtualDevice, DisplayWindowPolicyController dwpc, String packageName) {
final int callingUid = Binder.getCallingUid();
if (!validatePackageName(callingUid, packageName)) {
throw new SecurityException("packageName must match the calling uid");
@@ -1351,8 +1351,13 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- return createVirtualDisplayLocked(callback, projection, virtualDevice, callingUid,
+ final int displayId = createVirtualDisplayLocked(callback, projection, callingUid,
packageName, surface, flags, virtualDisplayConfig);
+ if (displayId != Display.INVALID_DISPLAY && virtualDevice != null && dwpc != null) {
+ mDisplayWindowPolicyControllers.put(displayId,
+ Pair.create(virtualDevice, dwpc));
+ }
+ return displayId;
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1360,8 +1365,7 @@
}
private int createVirtualDisplayLocked(IVirtualDisplayCallback callback,
- IMediaProjection projection, IVirtualDevice virtualDevice,
- int callingUid, String packageName, Surface surface,
+ IMediaProjection projection, int callingUid, String packageName, Surface surface,
int flags, VirtualDisplayConfig virtualDisplayConfig) {
if (mVirtualDisplayAdapter == null) {
Slog.w(TAG, "Rejecting request to create private virtual display "
@@ -1389,16 +1393,7 @@
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {
- final int displayId = display.getDisplayIdLocked();
- if (virtualDevice != null) {
- final VirtualDeviceManagerInternal vdm =
- getLocalService(VirtualDeviceManagerInternal.class);
- final DisplayWindowPolicyController controller =
- vdm.onVirtualDisplayCreated(virtualDevice, displayId);
- mDisplayWindowPolicyControllers.put(displayId,
- Pair.create(virtualDevice, controller));
- }
- return displayId;
+ return display.getDisplayIdLocked();
}
// Something weird happened and the logical display was not created.
@@ -3066,9 +3061,9 @@
@Override // Binder call
public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
IVirtualDisplayCallback callback, IMediaProjection projection,
- IVirtualDevice virtualDeviceToken, String packageName) {
+ String packageName) {
return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection,
- virtualDeviceToken, packageName);
+ null, null, packageName);
}
@Override // Binder call
@@ -3534,7 +3529,8 @@
return !Float.isNaN(refreshRate) && (refreshRate > 0.0f);
}
- private final class LocalService extends DisplayManagerInternal {
+ @VisibleForTesting
+ final class LocalService extends DisplayManagerInternal {
@Override
public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
@@ -3550,6 +3546,14 @@
}
@Override
+ public int createVirtualDisplay(VirtualDisplayConfig config,
+ IVirtualDisplayCallback callback, IVirtualDevice virtualDevice,
+ DisplayWindowPolicyController dwpc, String packageName) {
+ return createVirtualDisplayInternal(config, callback, null, virtualDevice, dwpc,
+ packageName);
+ }
+
+ @Override
public boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity) {
synchronized (mSyncRoot) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 9195b68..86969ce 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -247,6 +247,7 @@
private final ServiceConnection mVisibleConnection = new ServiceConnection() {
@Override public void onBindingDied(ComponentName name) {
synchronized (ImfLock.class) {
+ mService.invalidateAutofillSessionLocked();
if (mVisibleBound) {
unbindVisibleConnection();
}
@@ -257,6 +258,9 @@
}
@Override public void onServiceDisconnected(ComponentName name) {
+ synchronized (ImfLock.class) {
+ mService.invalidateAutofillSessionLocked();
+ }
}
};
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ea2b157..1a1c265 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -313,6 +313,15 @@
@Nullable
private CreateInlineSuggestionsRequest mPendingInlineSuggestionsRequest;
+ /**
+ * A callback into the autofill service obtained from the latest call to
+ * {@link #onCreateInlineSuggestionsRequestLocked}, which can be used to invalidate an
+ * autofill session in case the IME process dies.
+ */
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ private IInlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
+
@UserIdInt
private int mLastSwitchUserId;
@@ -2176,6 +2185,7 @@
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback,
boolean touchExplorationEnabled) {
clearPendingInlineSuggestionsRequestLocked();
+ mInlineSuggestionsRequestCallback = callback;
final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
try {
if (userId == mSettings.getCurrentUserId()
@@ -2797,6 +2807,7 @@
if (DEBUG) {
Slog.d(TAG, "Avoiding IME startup and unbinding current input method.");
}
+ invalidateAutofillSessionLocked();
mBindingController.unbindCurrentMethod();
return InputBindResult.NO_EDITOR;
}
@@ -2835,6 +2846,17 @@
}
@GuardedBy("ImfLock.class")
+ void invalidateAutofillSessionLocked() {
+ if (mInlineSuggestionsRequestCallback != null) {
+ try {
+ mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Cannot invalidate autofill session.", e);
+ }
+ }
+ }
+
+ @GuardedBy("ImfLock.class")
private boolean shouldPreventImeStartupLocked(
@NonNull String selectedMethodId,
@StartInputFlags int startInputFlags,
diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
index f9a8407..98bae3d 100644
--- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
+++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
@@ -105,7 +105,7 @@
@Override
protected void onDestroy() {
super.onDestroy();
- if (mAlert != null && mAlert.isShowing()) {
+ if (!isChangingConfigurations() && mAlert != null && mAlert.isShowing()) {
mAlert.dismiss();
}
mAlert = null;
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java
index 78e7b0a..7ca1978 100644
--- a/services/core/java/com/android/server/pm/AppsFilterBase.java
+++ b/services/core/java/com/android/server/pm/AppsFilterBase.java
@@ -321,6 +321,9 @@
|| targetPkgSetting.getAppId() < Process.FIRST_APPLICATION_UID
|| callingAppId == targetPkgSetting.getAppId()) {
return false;
+ } else if (Process.isSdkSandboxUid(callingAppId)) {
+ // we only allow sdk sandbox processes access to forcequeryable packages
+ return !isForceQueryable(targetPkgSetting.getAppId());
}
if (mCacheReady) { // use cache
if (!shouldFilterApplicationUsingCache(callingUid,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7c900ef..37bfbb1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2837,6 +2837,11 @@
if (!stagedSplits.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Full install must include a base package");
+ } else if ((params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
+ EventLog.writeEvent(0x534e4554, "219044664");
+
+ // Installing base.apk. Make sure the app is restarted.
+ params.setDontKillApp(false);
}
if (baseApk.isSplitRequired() && (stagedSplits.size() <= 1
|| !stagedSplitTypes.containsAll(requiredSplitTypes))) {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index f2bcd1d..d6c0ab6 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -192,11 +192,10 @@
/** The sequence id for trace. It is used to map the traces before resolving intent. */
private static int sTraceSeqId;
/** The trace format is "launchingActivity#$seqId:$state(:$packageName)". */
- final String mTraceName;
+ String mTraceName;
LaunchingState() {
if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- mTraceName = null;
return;
}
// Use an id because the launching app is not yet known before resolving intent.
@@ -205,8 +204,14 @@
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0);
}
- void stopTrace(boolean abort) {
+ void stopTrace(boolean abort, TransitionInfo endInfo) {
if (mTraceName == null) return;
+ if (!abort && endInfo != mAssociatedTransitionInfo) {
+ // Multiple TransitionInfo can be associated with the same LaunchingState (e.g. a
+ // launching activity launches another activity in a different windowing mode or
+ // display). Only the original associated info can emit a "completed" trace.
+ return;
+ }
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0);
final String launchResult;
if (mAssociatedTransitionInfo == null) {
@@ -218,6 +223,7 @@
}
// Put a supplement trace as the description of the async trace with the same id.
Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName + launchResult);
+ mTraceName = null;
}
@VisibleForTesting
@@ -321,7 +327,11 @@
mProcessSwitch = processSwitch;
mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs;
setLatestLaunchedActivity(r);
- launchingState.mAssociatedTransitionInfo = this;
+ // The launching state can be reused by consecutive launch. Its original association
+ // shouldn't be changed by a separated transition.
+ if (launchingState.mAssociatedTransitionInfo == null) {
+ launchingState.mAssociatedTransitionInfo = this;
+ }
if (options != null) {
final SourceInfo sourceInfo = options.getSourceInfo();
if (sourceInfo != null) {
@@ -908,7 +918,7 @@
return;
}
if (DEBUG_METRICS) Slog.i(TAG, "abort launch cause=" + cause);
- state.stopTrace(true /* abort */);
+ state.stopTrace(true /* abort */, null /* endInfo */);
launchObserverNotifyIntentFailed(state.mCurrentTransitionStartTimeNs);
}
@@ -924,7 +934,7 @@
Slog.i(TAG, "done abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
+ " info=" + info);
}
- info.mLaunchingState.stopTrace(abort);
+ info.mLaunchingState.stopTrace(abort, info);
stopLaunchTrace(info);
final Boolean isHibernating =
mLastHibernationStates.remove(info.mLastLaunchedActivity.packageName);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2824d54..7723a46 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1365,10 +1365,9 @@
PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
if (balAllowedByPiSender && realCallingUid != callingUid) {
- // If the caller is a legacy app, we won't check if the caller has BAL permission.
- final boolean isPiBalOptionEnabled = CompatChanges.isChangeEnabled(
- ENABLE_PENDING_INTENT_BAL_OPTION, realCallingUid);
- if (isPiBalOptionEnabled && ActivityManager.checkComponentPermission(
+ final boolean useCallerPermission =
+ PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions);
+ if (useCallerPermission && ActivityManager.checkComponentPermission(
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
realCallingUid, -1, true)
== PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 35f977d..95de040 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -146,7 +146,6 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
-import com.android.server.am.HostingRecord;
import com.android.server.am.UserState;
import com.android.server.utils.Slogf;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
@@ -1057,9 +1056,7 @@
r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
final boolean isTop = andResume && r.isTopRunningActivity();
- mService.startProcessAsync(r, knownToBeDead, isTop,
- isTop ? HostingRecord.HOSTING_TYPE_TOP_ACTIVITY
- : HostingRecord.HOSTING_TYPE_ACTIVITY);
+ mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index e79e77c..61deb59 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -20,7 +20,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM;
import android.annotation.IntDef;
import android.os.HandlerExecutor;
@@ -224,7 +224,7 @@
if (DEBUG) Slog.d(TAG, "finishOp fade-in " + windowToken.getTopChild());
// The previous animation leash will be dropped when preparing fade-in animation, so
// simply apply new animation without restoring the transformation.
- fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+ fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
} else if (op.mAction == Operation.ACTION_SEAMLESS && mRotator != null
&& op.mLeash != null && op.mLeash.isValid()) {
if (DEBUG) Slog.d(TAG, "finishOp undo seamless " + windowToken.getTopChild());
@@ -298,7 +298,7 @@
final WindowToken windowToken = mTargetWindowTokens.keyAt(i);
final Operation op = mTargetWindowTokens.valueAt(i);
if (op.mAction == Operation.ACTION_FADE) {
- fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+ fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
op.mLeash = windowToken.getAnimationLeash();
if (DEBUG) Slog.d(TAG, "Start fade-out " + windowToken.getTopChild());
} else if (op.mAction == Operation.ACTION_SEAMLESS) {
@@ -332,7 +332,7 @@
mHideImmediately = true;
final Operation op = new Operation(Operation.ACTION_FADE);
mTargetWindowTokens.put(windowToken, op);
- fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+ fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
op.mLeash = windowToken.getAnimationLeash();
mHideImmediately = original;
if (DEBUG) Slog.d(TAG, "hideImmediately " + windowToken.getTopChild());
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 94f4382..a059ac6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -225,6 +225,7 @@
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
import android.window.TransitionRequestInfo;
@@ -1960,6 +1961,16 @@
}
/**
+ * @see DisplayWindowPolicyController#canShowTasksInRecents()
+ */
+ boolean canShowTasksInRecents() {
+ if (mDwpcHelper == null) {
+ return true;
+ }
+ return mDwpcHelper.canShowTasksInRecents();
+ }
+
+ /**
* Applies the rotation transaction. This must be called after {@link #updateRotationUnchecked}
* (if it returned {@code true}) to actually finish the rotation.
*
diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
index 2deb828..5d49042 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -152,6 +152,16 @@
return mDisplayWindowPolicyController.isWindowingModeSupported(windowingMode);
}
+ /**
+ * @see DisplayWindowPolicyController#canShowTasksInRecents()
+ */
+ public final boolean canShowTasksInRecents() {
+ if (mDisplayWindowPolicyController == null) {
+ return true;
+ }
+ return mDisplayWindowPolicyController.canShowTasksInRecents();
+ }
+
void dump(String prefix, PrintWriter pw) {
if (mDisplayWindowPolicyController != null) {
pw.println();
diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
index 0ae119a..2e5474e 100644
--- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM;
import android.annotation.NonNull;
import android.view.SurfaceControl;
@@ -83,7 +83,7 @@
final AsyncRotationController controller =
mDisplayContent.getAsyncRotationController();
final Runnable fadeAnim = () -> fadeWindowToken(show, mNavigationBar.mToken,
- ANIMATION_TYPE_APP_TRANSITION);
+ ANIMATION_TYPE_TOKEN_TRANSFORM);
if (controller == null) {
fadeAnim.run();
} else if (!controller.isTargetToken(mNavigationBar.mToken)) {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index e259238..4860762 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1390,6 +1390,13 @@
return false;
}
+ // Ignore the task if it is started on a display which is not allow to show its tasks on
+ // Recents.
+ if (task.getDisplayContent() != null
+ && !task.getDisplayContent().canShowTasksInRecents()) {
+ return false;
+ }
+
return true;
}
@@ -1399,13 +1406,19 @@
private boolean isInVisibleRange(Task task, int taskIndex, int numVisibleTasks,
boolean skipExcludedCheck) {
if (!skipExcludedCheck) {
- // Keep the most recent task even if it is excluded from recents
+ // Keep the most recent task of home display even if it is excluded from recents.
final boolean isExcludeFromRecents =
(task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
== FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
if (isExcludeFromRecents) {
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
- return taskIndex == 0;
+ if (DEBUG_RECENTS_TRIM_TASKS) {
+ Slog.d(TAG,
+ "\texcludeFromRecents=true, taskIndex = " + taskIndex
+ + ", isOnHomeDisplay: " + task.isOnHomeDisplay());
+ }
+ // The Recents is only supported on default display now, we should only keep the
+ // most recent task of home display.
+ return (task.isOnHomeDisplay() && taskIndex == 0);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 7240fd5..ef18b50 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2114,6 +2114,11 @@
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
// Set the launch bounds for launch-into-pip Activity on the root task.
if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
+ // Record the snapshot now, it will be later fetched for content-pip animation.
+ // We do this early in the process to make sure the right snapshot is used for
+ // entering content-pip animation.
+ mWindowManager.mTaskSnapshotController.recordTaskSnapshot(
+ task, false /* allowSnapshotHome */);
rootTask.setBounds(r.getOptions().getLaunchBounds());
}
rootTask.setDeferTaskAppear(false);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index ccd018f..e8445ab 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -159,9 +159,7 @@
return null;
}
if (topFullscreenActivity.getWindowConfiguration().getRotation()
- != taskSnapshot.getRotation()
- // Use normal rotation to avoid flickering of IME window in old orientation.
- && !taskSnapshot.hasImeSurface()) {
+ != taskSnapshot.getRotation()) {
// The snapshot should have been checked by ActivityRecord#isSnapshotCompatible
// that the activity will be updated to the same rotation as the snapshot. Since
// the transition is not started yet, fixed rotation transform needs to be applied
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 3dde2f1..ca86db9 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -553,10 +553,10 @@
public static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
/**
- * Animation when a fixed rotation transform is applied to a window token.
+ * Animation applied to a non-app window token, e.g. a fixed rotation transform.
* @hide
*/
- public static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
+ public static final int ANIMATION_TYPE_TOKEN_TRANSFORM = 1 << 6;
/**
* Animation when a reveal starting window animation is applied to app window.
@@ -582,7 +582,7 @@
ANIMATION_TYPE_RECENTS,
ANIMATION_TYPE_WINDOW_ANIMATION,
ANIMATION_TYPE_INSETS_CONTROL,
- ANIMATION_TYPE_FIXED_TRANSFORM,
+ ANIMATION_TYPE_TOKEN_TRANSFORM,
ANIMATION_TYPE_STARTING_REVEAL
})
@Retention(RetentionPolicy.SOURCE)
@@ -600,7 +600,7 @@
case ANIMATION_TYPE_RECENTS: return "recents_animation";
case ANIMATION_TYPE_WINDOW_ANIMATION: return "window_animation";
case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation";
- case ANIMATION_TYPE_FIXED_TRANSFORM: return "fixed_rotation";
+ case ANIMATION_TYPE_TOKEN_TRANSFORM: return "token_transform";
case ANIMATION_TYPE_STARTING_REVEAL: return "starting_reveal";
default: return "unknown type:" + type;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index dc6e58a..324f029 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -96,7 +96,6 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
-import com.android.server.am.HostingRecord;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.FileDescriptor;
@@ -1148,8 +1147,7 @@
// for the current activity to be paused.
final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
- isTop ? HostingRecord.HOSTING_TYPE_NEXT_TOP_ACTIVITY
- : HostingRecord.HOSTING_TYPE_NEXT_ACTIVITY);
+ isTop ? "pre-top-activity" : "pre-activity");
}
if (lastResumed != null) {
lastResumed.setWillCloseOrEnterPip(true);
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 912fdb2..43dc9c8 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -116,6 +116,9 @@
return;
}
listener.unregister();
+ if (listener.mDeathRecipient != null) {
+ listener.mDeathRecipient.unlinkToDeath();
+ }
}
void dispatchPendingConfigurationIfNeeded(int displayId) {
diff --git a/services/tests/servicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java b/services/tests/servicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
new file mode 100644
index 0000000..9474253
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.fullbackup;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
+
+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 PerformFullTransportBackupTaskTest {
+ @Mock
+ UserBackupManagerService mBackupManagerService;
+ @Mock
+ TransportManager mTransportManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mBackupManagerService.getTransportManager()).thenReturn(mTransportManager);
+ }
+
+ @Test
+ public void testNewWithCurrentTransport_noTransportConnection_throws() {
+ when(mTransportManager.getCurrentTransportClient(any())).thenReturn(null);
+
+ assertThrows(IllegalStateException.class,
+ () -> {
+ PerformFullTransportBackupTask task = PerformFullTransportBackupTask
+ .newWithCurrentTransport(
+ mBackupManagerService,
+ /* operationStorage */ null,
+ /* observer */ null,
+ /* whichPackages */ null,
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ /* latch */ null,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ /* caller */ null,
+ /* backupEligibilityRules */ null);
+ });
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index f9671e5..f242fda 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -228,36 +228,36 @@
@Test
public void onVirtualDisplayRemovedLocked_doesNotThrowException() {
- final int displayId = 2;
- mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
// This call should not throw any exceptions.
- mDeviceImpl.onVirtualDisplayRemovedLocked(displayId);
+ mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID);
}
@Test
public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
- final int displayId = 2;
- mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
nullable(String.class), anyInt(), eq(null));
- TestableLooper.get(this).processAllMessages();
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId), eq(null));
+ nullable(String.class), eq(DISPLAY_ID), eq(null));
}
@Test
public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired()
throws RemoteException {
- final int displayId = 2;
- mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+ GenericWindowPolicyController gwpc = mDeviceImpl.createWindowPolicyController();
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
assertThrows(IllegalStateException.class,
- () -> mDeviceImpl.onVirtualDisplayCreatedLocked(displayId));
+ () -> mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID));
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId), eq(null));
+ nullable(String.class), eq(DISPLAY_ID), eq(null));
}
@Test
@@ -269,30 +269,30 @@
@Test
public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException {
- final int displayId = 2;
- mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId), eq(null));
+ nullable(String.class), eq(DISPLAY_ID), eq(null));
IBinder wakeLock = wakeLockCaptor.getValue();
- mDeviceImpl.onVirtualDisplayRemovedLocked(displayId);
+ mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID);
verify(mIPowerManagerMock, Mockito.times(1)).releaseWakeLock(eq(wakeLock), anyInt());
}
@Test
public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException {
- final int displayId = 2;
- mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId), eq(null));
+ nullable(String.class), eq(DISPLAY_ID), eq(null));
IBinder wakeLock = wakeLockCaptor.getValue();
// Close the VirtualDevice without first notifying it of the VirtualDisplay removal.
@@ -416,7 +416,8 @@
@Test
public void onAudioSessionStarting_hasVirtualAudioController() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback);
@@ -425,7 +426,8 @@
@Test
public void onAudioSessionEnded_noVirtualAudioController() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback);
mDeviceImpl.onAudioSessionEnded();
@@ -435,7 +437,8 @@
@Test
public void close_cleanVirtualAudioController() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback);
mDeviceImpl.close();
@@ -659,7 +662,8 @@
@Test
public void openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -677,7 +681,8 @@
@Test
public void openPermissionControllerOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -695,7 +700,8 @@
@Test
public void openSettingsOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -713,7 +719,8 @@
@Test
public void openVendingOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -731,7 +738,8 @@
@Test
public void openGoogleDialerOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
@@ -749,7 +757,8 @@
@Test
public void openGoogleMapsOnVirtualDisplay_startBlockedAlertActivity() {
- mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index cd836c7..e2c3a94 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.companion.virtual.audio;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
import static android.media.AudioAttributes.FLAG_SECURE;
import static android.media.AudioPlaybackConfiguration.PLAYER_STATE_STARTED;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -81,7 +82,8 @@
/* blockedActivities= */ new ArraySet<>(),
VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED,
/* activityListener= */ null,
- /* activityBlockedCallback= */ null);
+ /* activityBlockedCallback= */ null,
+ /* deviceProfile= */ DEVICE_PROFILE_APP_STREAMING);
}
@Test
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 6f48368..1e97c1c 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -45,6 +45,7 @@
import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -63,6 +64,7 @@
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.DisplayWindowPolicyController;
import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
@@ -220,7 +222,7 @@
builder.setUniqueId(uniqueId);
builder.setFlags(flags);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
+ null /* projection */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -346,7 +348,7 @@
builder.setFlags(flags);
builder.setUniqueId(uniqueId);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
+ null /* projection */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -586,8 +588,7 @@
VIRTUAL_DISPLAY_NAME, width, height, dpi);
builder.setUniqueId(uniqueId);
final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */,
- PACKAGE_NAME);
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
// The second virtual display requests to mirror the first virtual display.
final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
@@ -598,7 +599,7 @@
builder2.setDisplayIdToMirror(firstDisplayId);
final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
mMockAppToken2 /* callback */, null /* projection */,
- null /* virtualDeviceToken */, PACKAGE_NAME);
+ PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
// flush the handler
@@ -636,8 +637,7 @@
builder.setSurface(surface);
builder.setUniqueId(uniqueId);
final int displayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */,
- PACKAGE_NAME);
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -669,7 +669,7 @@
builder.setUniqueId("uniqueId --- OWN_DISPLAY_GROUP");
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
+ null /* projection */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
@@ -700,7 +700,7 @@
try {
bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME);
+ null /* projection */, PACKAGE_NAME);
fail("Creating virtual display with VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP without "
+ "ADD_TRUSTED_DISPLAY permission should throw SecurityException.");
} catch (SecurityException e) {
@@ -716,6 +716,8 @@
public void testOwnDisplayGroup_allowCreationWithVirtualDevice() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+
registerDefaultDisplays(displayManager);
DisplayManagerService.BinderService bs = displayManager.new BinderService();
@@ -733,8 +735,9 @@
when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice))
.thenReturn(true);
- int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
- null /* projection */, virtualDevice /* virtualDeviceToken */, PACKAGE_NAME);
+ int displayId = localService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, virtualDevice /* virtualDeviceToken */,
+ mock(DisplayWindowPolicyController.class), PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
index 9674ebd..facbe80 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
@@ -1336,6 +1336,46 @@
}
+ @Test
+ public void testSdkSandbox_canSeeForceQueryable() throws Exception {
+ final AppsFilterImpl appsFilter =
+ new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
+ mMockHandler);
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady(mPmInternal);
+
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID,
+ setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+
+ int callingUid = 20123;
+ assertTrue(Process.isSdkSandboxUid(callingUid));
+
+ assertFalse(
+ appsFilter.shouldFilterApplication(mSnapshot, callingUid,
+ null /* callingSetting */, target, SYSTEM_USER));
+ }
+
+ @Test
+ public void testSdkSandbox_cannotSeeNonForceQueryable() throws Exception {
+ final AppsFilterImpl appsFilter =
+ new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
+ mMockHandler);
+ simulateAddBasicAndroid(appsFilter);
+ appsFilter.onSystemReady(mPmInternal);
+
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkg("com.some.package"), DUMMY_TARGET_APPID,
+ setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+
+ int callingUid = 20123;
+ assertTrue(Process.isSdkSandboxUid(callingUid));
+
+ assertTrue(
+ appsFilter.shouldFilterApplication(mSnapshot, callingUid,
+ null /* callingSetting */, target, SYSTEM_USER));
+ }
+
private List<Integer> toList(int[] array) {
ArrayList<Integer> ret = new ArrayList<>(array.length);
for (int i = 0; i < array.length; i++) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 2fea228..5b909a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -532,6 +532,9 @@
transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
setLastExpectedStartedId(activityOnNewDisplay);
transitToDrawnAndVerifyOnLaunchFinished(activityOnNewDisplay);
+
+ assertWithMessage("The launching state must not include the separated launch")
+ .that(mLaunchingState.contains(activityOnNewDisplay)).isFalse();
}
@Test
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 32f3bfe..0792300 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -78,7 +78,7 @@
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -1439,7 +1439,7 @@
displayContent.setRotationAnimation(rotationAnim);
// The fade rotation animation also starts to hide some non-app windows.
assertNotNull(displayContent.getAsyncRotationController());
- assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
+ assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
for (WindowState w : windows) {
w.setOrientationChanging(true);
@@ -1493,10 +1493,10 @@
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
assertNotNull(asyncRotationController);
- assertTrue(mStatusBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
- assertTrue(mNavBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
+ assertTrue(mStatusBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
+ assertTrue(mNavBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
// Notification shade may have its own view animation in real case so do not fade out it.
- assertFalse(mNotificationShadeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
+ assertFalse(mNotificationShadeWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
// If the visibility of insets state is changed, the rotated state should be updated too.
final InsetsState rotatedState = app.getFixedRotationTransformInsetsState();
@@ -1567,7 +1567,7 @@
app.token, app.token, mDisplayContent.mDisplayId);
assertTrue(asyncRotationController.isTargetToken(mImeWindow.mToken));
assertTrue(mImeWindow.mToken.hasFixedRotationTransform());
- assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
+ assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
// The fixed rotation transform can only be finished when all animation finished.
doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
index 02009b7..47c2176 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
@@ -242,5 +242,10 @@
mRunningUids.clear();
mRunningUids.addAll(runningUids);
}
+
+ @Override
+ public boolean canShowTasksInRecents() {
+ return true;
+ }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 2847283..feb1c6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -691,6 +691,20 @@
}
@Test
+ public void testVisibleTasks_excludedFromRecents_nonDefaultDisplayTaskNotVisible() {
+ Task excludedTaskOnVirtualDisplay = createTaskBuilder(".excludedTaskOnVirtualDisplay")
+ .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ .build();
+ excludedTaskOnVirtualDisplay.mUserSetupComplete = true;
+ doReturn(false).when(excludedTaskOnVirtualDisplay).isOnHomeDisplay();
+ mRecentTasks.add(mTasks.get(0));
+ mRecentTasks.add(excludedTaskOnVirtualDisplay);
+
+ // Expect that the first visible excluded-from-recents task is visible
+ assertGetRecentTasksOrder(0 /* flags */, mTasks.get(0));
+ }
+
+ @Test
public void testVisibleTasks_excludedFromRecents_withExcluded() {
// Create some set of tasks, some of which are visible and some are not
Task t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
@@ -802,6 +816,17 @@
}
@Test
+ public void testVisibleTask_displayCanNotShowTaskFromRecents_expectNotVisible() {
+ final DisplayContent displayContent = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+ doReturn(false).when(displayContent).canShowTasksInRecents();
+ final Task task = displayContent.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ mRecentTasks.add(task);
+
+ assertFalse(mRecentTasks.isVisibleRecentTask(task));
+ }
+
+ @Test
public void testFreezeTaskListOrder_reorderExistingTask() {
// Add some tasks
mRecentTasks.add(mTasks.get(0));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index eba276f..8546763 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -36,6 +36,7 @@
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -595,6 +596,7 @@
eq(mDefaultDisplay.mDisplayId), eq(true));
verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
assertFalse(mController.isNavigationBarAttachedToApp());
+ assertTrue(navToken.isAnimating(ANIMATION_TYPE_TOKEN_TRANSFORM));
}
@Test
@@ -622,6 +624,7 @@
verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
assertFalse(mController.isNavigationBarAttachedToApp());
+ assertFalse(navToken.isAnimating(ANIMATION_TYPE_TOKEN_TRANSFORM));
}
@Test
@@ -649,6 +652,7 @@
eq(mDefaultDisplay.mDisplayId), eq(true));
verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
assertFalse(mController.isNavigationBarAttachedToApp());
+ assertTrue(navToken.isAnimating(ANIMATION_TYPE_TOKEN_TRANSFORM));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 35d8129..891b33b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -108,6 +108,13 @@
@Presubmit
@RunWith(WindowTestRunner.class)
public class SizeCompatTests extends WindowTestsBase {
+ private static final String CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS =
+ "never_constrain_display_apis";
+ private static final String CONFIG_ALWAYS_CONSTRAIN_DISPLAY_APIS =
+ "always_constrain_display_apis";
+ private static final String CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES =
+ "never_constrain_display_apis_all_packages";
+
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -123,8 +130,10 @@
doReturn(mActivityMetricsLogger).when(mAtm.mTaskSupervisor).getActivityMetricsLogger();
mInitialConstrainDisplayApisFlags = DeviceConfig.getProperties(
NAMESPACE_CONSTRAIN_DISPLAY_APIS);
- DeviceConfig.setProperties(
- new Properties.Builder(NAMESPACE_CONSTRAIN_DISPLAY_APIS).build());
+ // Provide empty default values for the configs.
+ setNeverConstrainDisplayApisFlag("", true);
+ setNeverConstrainDisplayApisAllPackagesFlag(false, true);
+ setAlwaysConstrainDisplayApisFlag("", true);
}
@After
@@ -805,9 +814,9 @@
public void testNeverConstrainDisplayApisDeviceConfig_allPackagesFlagTrue_sandboxNotApplied() {
setUpDisplaySizeWithApp(1000, 1200);
- setNeverConstrainDisplayApisAllPackagesFlag("true");
+ setNeverConstrainDisplayApisAllPackagesFlag(true, false);
// Setting 'never_constrain_display_apis' as well to make sure it is ignored.
- setNeverConstrainDisplayApisFlag("com.android.other::,com.android.other2::");
+ setNeverConstrainDisplayApisFlag("com.android.other::,com.android.other2::", false);
// Make the task root resizable.
mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -830,7 +839,7 @@
setNeverConstrainDisplayApisFlag(
"com.android.frameworks.wmtests:20:,com.android.other::,"
- + "com.android.frameworks.wmtests:0:10");
+ + "com.android.frameworks.wmtests:0:10", false);
// Make the task root resizable.
mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -851,7 +860,8 @@
public void testNeverConstrainDisplayApisDeviceConfig_packageOutsideRange_sandboxingApplied() {
setUpDisplaySizeWithApp(1000, 1200);
- setNeverConstrainDisplayApisFlag("com.android.other::,com.android.frameworks.wmtests:1:5");
+ setNeverConstrainDisplayApisFlag("com.android.other::,com.android.frameworks.wmtests:1:5",
+ false);
// Make the task root resizable.
mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -870,7 +880,7 @@
public void testNeverConstrainDisplayApisDeviceConfig_packageNotInFlag_sandboxingApplied() {
setUpDisplaySizeWithApp(1000, 1200);
- setNeverConstrainDisplayApisFlag("com.android.other::,com.android.other2::");
+ setNeverConstrainDisplayApisFlag("com.android.other::,com.android.other2::", false);
// Make the task root resizable.
mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -952,7 +962,7 @@
setAlwaysConstrainDisplayApisFlag(
"com.android.frameworks.wmtests:20:,com.android.other::,"
- + "com.android.frameworks.wmtests:0:10");
+ + "com.android.frameworks.wmtests:0:10", false);
// Make the task root resizable.
mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -2562,19 +2572,22 @@
displayContent.onRequestedOverrideConfigurationChanged(c);
}
- private static void setNeverConstrainDisplayApisFlag(@Nullable String value) {
- DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS, "never_constrain_display_apis",
- value, /* makeDefault= */ false);
- }
-
- private static void setNeverConstrainDisplayApisAllPackagesFlag(@Nullable String value) {
+ private static void setNeverConstrainDisplayApisFlag(@Nullable String value,
+ boolean makeDefault) {
DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
- "never_constrain_display_apis_all_packages",
- value, /* makeDefault= */ false);
+ CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS, value, makeDefault);
}
- private static void setAlwaysConstrainDisplayApisFlag(@Nullable String value) {
- DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS, "always_constrain_display_apis",
- value, /* makeDefault= */ false);
+ private static void setNeverConstrainDisplayApisAllPackagesFlag(boolean value,
+ boolean makeDefault) {
+ DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, String.valueOf(value),
+ makeDefault);
+ }
+
+ private static void setAlwaysConstrainDisplayApisFlag(@Nullable String value,
+ boolean makeDefault) {
+ DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+ CONFIG_ALWAYS_CONSTRAIN_DISPLAY_APIS, value, makeDefault);
}
}
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 646647f..1685673 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -24,6 +24,7 @@
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.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
@@ -86,7 +87,7 @@
assertEquals(1, mController.mListeners.size());
- final IBinder clientToken = new Binder();
+ final IBinder clientToken = mock(IBinder.class);
mController.registerWindowContainerListener(clientToken, mContainer, -1,
TYPE_APPLICATION_OVERLAY, null /* options */);
@@ -103,6 +104,10 @@
WindowContextListenerController.WindowContextListenerImpl listener =
mController.mListeners.get(mClientToken);
assertEquals(container, listener.getWindowContainer());
+
+ mController.unregisterWindowContainerListener(clientToken);
+ assertFalse(mController.mListeners.containsKey(clientToken));
+ verify(clientToken).unlinkToDeath(any(), anyInt());
}
@UseTestDisplay
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 39923a2..42d446d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -119,6 +119,7 @@
// TODO: These constants need to be refined.
private static final long VALIDATION_TIMEOUT_MILLIS = 4000;
private static final long MAX_UPDATE_TIMEOUT_MILLIS = 30000;
+ private static final long EXTERNAL_HOTWORD_CLEANUP_MILLIS = 2000;
private static final Duration MAX_UPDATE_TIMEOUT_DURATION =
Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS);
private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
@@ -854,6 +855,7 @@
int bytesRead = source.read(buffer, 0, 1024);
if (bytesRead < 0) {
+ Slog.i(TAG, "Reached end of stream for external hotword");
break;
}
@@ -864,6 +866,12 @@
}
} catch (IOException e) {
Slog.w(TAG, "Failed supplying audio data to validator", e);
+
+ try {
+ callback.onError();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to report onError status: " + ex);
+ }
} finally {
synchronized (mLock) {
mCurrentAudioSink = null;
@@ -874,51 +882,68 @@
// TODO: handle cancellations well
// TODO: what if we cancelled and started a new one?
mRemoteHotwordDetectionService.run(
- service -> service.detectFromMicrophoneSource(
- serviceAudioSource,
- // TODO: consider making a proxy callback + copy of audio format
- AUDIO_SOURCE_EXTERNAL,
- audioFormat,
- options,
- new IDspHotwordDetectionCallback.Stub() {
- @Override
- public void onRejected(HotwordRejectedResult result)
- throws RemoteException {
- bestEffortClose(serviceAudioSink);
- bestEffortClose(serviceAudioSource);
- bestEffortClose(audioSource);
+ service -> {
+ service.detectFromMicrophoneSource(
+ serviceAudioSource,
+ // TODO: consider making a proxy callback + copy of audio format
+ AUDIO_SOURCE_EXTERNAL,
+ audioFormat,
+ options,
+ new IDspHotwordDetectionCallback.Stub() {
+ @Override
+ public void onRejected(HotwordRejectedResult result)
+ throws RemoteException {
+ mScheduledExecutorService.schedule(
+ () -> {
+ bestEffortClose(serviceAudioSink, audioSource);
+ },
+ EXTERNAL_HOTWORD_CLEANUP_MILLIS,
+ TimeUnit.MILLISECONDS);
- if (mDebugHotwordLogging && result != null) {
- Slog.i(TAG, "Egressed rejected result: " + result);
- }
- // TODO: Propagate the HotwordRejectedResult.
- }
+ callback.onRejected(result);
- @Override
- public void onDetected(HotwordDetectedResult triggerResult)
- throws RemoteException {
- bestEffortClose(serviceAudioSink);
- bestEffortClose(serviceAudioSource);
- try {
- enforcePermissionsForDataDelivery();
- } catch (SecurityException e) {
- bestEffortClose(audioSource);
- callback.onError();
- return;
- }
- callback.onDetected(triggerResult, null /* audioFormat */,
- null /* audioStream */);
- if (triggerResult != null) {
- Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(
- triggerResult) + " bits from hotword trusted process");
- if (mDebugHotwordLogging) {
- Slog.i(TAG, "Egressed detected result: " + triggerResult);
+ if (result != null) {
+ Slog.i(TAG, "Egressed 'hotword rejected result' "
+ + "from hotword trusted process");
+ if (mDebugHotwordLogging) {
+ Slog.i(TAG, "Egressed detected result: " + result);
+ }
}
}
- // TODO: Add a delay before closing.
- bestEffortClose(audioSource);
- }
- }));
+
+ @Override
+ public void onDetected(HotwordDetectedResult triggerResult)
+ throws RemoteException {
+ mScheduledExecutorService.schedule(
+ () -> {
+ bestEffortClose(serviceAudioSink, audioSource);
+ },
+ EXTERNAL_HOTWORD_CLEANUP_MILLIS,
+ TimeUnit.MILLISECONDS);
+
+ try {
+ enforcePermissionsForDataDelivery();
+ } catch (SecurityException e) {
+ callback.onError();
+ return;
+ }
+ callback.onDetected(triggerResult, null /* audioFormat */,
+ null /* audioStream */);
+ if (triggerResult != null) {
+ Slog.i(TAG, "Egressed "
+ + HotwordDetectedResult.getUsageSize(triggerResult)
+ + " bits from hotword trusted process");
+ if (mDebugHotwordLogging) {
+ Slog.i(TAG,
+ "Egressed detected result: " + triggerResult);
+ }
+ }
+ }
+ });
+
+ // A copy of this has been created and passed to the hotword validator
+ bestEffortClose(serviceAudioSource);
+ });
}
private class ServiceConnectionFactory {
@@ -1118,6 +1143,12 @@
});
}
+ private static void bestEffortClose(Closeable... closeables) {
+ for (Closeable closeable : closeables) {
+ bestEffortClose(closeable);
+ }
+ }
+
private static void bestEffortClose(Closeable closeable) {
try {
closeable.close();
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 249f740..e5e8f0a 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -162,7 +162,8 @@
@Deprecated
public String getIccId() {
if (mIccIdAccessRestricted) {
- throw new UnsupportedOperationException("getIccId from UiccPortInfo");
+ throw new UnsupportedOperationException("getIccId() is not supported by UiccCardInfo."
+ + " Please Use UiccPortInfo API instead");
}
//always return ICCID from first port.
return getPorts().stream().findFirst().get().getIccId();
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index dd3639a..06c5b5c 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -161,7 +161,8 @@
@Deprecated
public boolean getIsActive() {
if (mLogicalSlotAccessRestricted) {
- throw new UnsupportedOperationException("get port status from UiccPortInfo");
+ throw new UnsupportedOperationException("getIsActive() is not supported by "
+ + "UiccSlotInfo. Please Use UiccPortInfo API instead");
}
//always return status from first port.
return getPorts().stream().findFirst().get().isActive();
@@ -198,7 +199,8 @@
@Deprecated
public int getLogicalSlotIdx() {
if (mLogicalSlotAccessRestricted) {
- throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
+ throw new UnsupportedOperationException("getLogicalSlotIdx() is not supported by "
+ + "UiccSlotInfo. Please use UiccPortInfo API instead");
}
//always return logical slot index from first port.
//portList always have at least one element.